spoj LCMSUM

题目

题目链接
给定N,求 Ni=1Lcm(i,N)
共T组测试数据。
1 <= T <= 300000
1 <= n <= 1000000

题解

我们考虑将lcm变为gcd,再枚举gcd的值。

i=1Nlcm(i,N)=i=1NiNgcd(i,N)=d|Ni=1NiNd[gcd(i,N)=d]=d|NNi=1N/di[gcd(i,N/d)=1]

注意到当N>1时 gcd(n,i)=1 gcd(n,ni)=1 也就是说与N互质的i是成对出现的。那就有 Ni=1i[gcd(i,N)=1]=Nφ(N)2 。那么原式中:
d|NNi=1N/di[gcd(i,N/d)=1]=N(d=N)=d|NNN/dφ(N/d)2(dN)

因为d与N/d是等价的,所以有:
i=1Nlcm(i,N)=Nd|N,d>1dφ(d)2+N

我们只需用线性筛求出 φ(i) 在枚举N的因子就可以求出结果了。
但是T很大,每一组都这么做的话会超时。其实我们可以预处理出所有答案。
考虑一个因子d,它对所有d的倍数都贡献了 dφ(d)2 我们可以枚举d与它的倍数。

for(int i=1;i<=1000000;i++)
 for(int j=1;j*i<=1000000;j++) ans[i*j]+=(ll)j*phi[j]/2;
for(int i=1;i<=1000000;i++)ans[i]=(ll)i*ans[i]+i;

它的复杂度是可以用调和级数证明的。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;

#define maxn 1000100
typedef long long ll;
int phi[maxn],p[100010],num,x,T;
ll ans[maxn];
bool b[maxn];

void init(){
    phi[1]=1;
    for(int i=2;i<=1000000;i++){
        if(!b[i]){
            p[++num]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=num;j++){
            if(p[j]*i>1000000)break;
            b[i*p[j]]=true;
            if(i%p[j]==0){
                phi[i*p[j]]=phi[i]*p[j];
                break;
            }
            phi[i*p[j]]=phi[i]*(p[j]-1);
        }
    }
    for(int i=1;i<=1000000;i++)
     for(int j=1;j*i<=1000000;j++) ans[i*j]+=(ll)j*phi[j]/2;
    for(int i=1;i<=1000000;i++)ans[i]=(ll)i*ans[i]+i;
}

int main(){
    init();
    scanf("%d",&T);
    while(T--){
        scanf("%d",&x);
        printf("%lld\n",ans[x]);
    }
    return 0;
}

你可能感兴趣的:(spoj LCMSUM)