【题目】
洛谷传送门
Description
Given n, calculate the sum LCM(1,n) + LCM(2,n) + … + LCM(n,n), where LCM(i,n) denotes the Least Common Multiple of the integers i and n.
Input
The first line contains T the number of test cases. Each of the next T lines contain an integer n.
Output
Output T lines, one for each test case, containing the required sum.
Sample Input
3
1
2
5
Sample Output
1
4
55
Hint
Constraints 1 <= T <= 300000 1 <= n <= 1000000
【分析】
我们要求的是
i=1∑nlcm(i,n)
把
lcm 换成
gcd 就是
i=1∑ngcd(i,n)i×n
这个式子可以巧妙的转换成
21(i=1∑n−1gcd(i,n)i×n+i=1∑n−1gcd(n−i,n)(n−i)×n)+n
根据辗转相减法,
gcd(i,n)=gcd(n−i,n),因此可以继续化简
21i=1∑n−1gcd(i,n)n2+n
也就是说,我们现在的目标就是求出
i=1∑n−1gcd(i,n)n2
我们枚举
gcd,把式子转化成
d∣n∑dn2i=1∑n−1[gcd(i,n)=d]
转换为
gcd=1 的形式
d∣n∑dn2i=1,i̸=n∑dn[gcd(i,dn)=1]
发现后面部分跟欧拉函数的解析式一样(
φ(n)=i=1∑n[gcd(i,n)=1])
用欧拉函数转换一下就是
d∣n,d̸=n∑dn2×φ(dn)
把一个
n 提出来,也就是
nd∣n,d̸=n∑dn×φ(dn)=nd∣n,d̸=1∑d×φ(d)
然后线性筛出欧拉函数值,预处理一下就可以了。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;
bool mark[N];
int prime[N],phi[N];
long long ans[N];
void linear_sieves()
{
int i,j,sum=0;
memset(mark,true,sizeof(mark));
mark[0]=mark[1]=false;phi[1]=1;
for(i=2;i<N;++i)
{
if(mark[i]) prime[++sum]=i,phi[i]=i-1;
for(j=1;j<=sum&&i*prime[j]<N;++j)
{
mark[i*prime[j]]=false;
if(i%prime[j]) phi[i*prime[j]]=phi[i]*phi[prime[j]];
else {phi[i*prime[j]]=phi[i]*prime[j];break;}
}
}
}
void prework()
{
int i,j;
for(i=1;i<N;++i)
for(j=2;i*j<N;++j)
ans[i*j]+=1ll*j*phi[j];
for(i=1;i<N;++i) ans[i]=ans[i]*i/2+i;
}
int main()
{
int n,i,x;
scanf("%d",&n);
linear_sieves();
prework();
for(i=1;i<=n;++i)
{
scanf("%d",&x);
printf("%lld\n",ans[x]);
}
return 0;
}