bzoj2226[Spoj 5971] LCMSum

题目链接:bzoj2226
题目大意:
多组数据。给定n,求 ni=1lcm(in)
1 <= T <= 300000,1 <= n <= 1000000

题解:
线性筛、欧拉函数

i=1nlcm(in)=ni=1nigcd(i,n)

省略前面的,套路化简:
ni=1nd|i,d|nid[gcd(i,n)=d]

交换枚举倍数和约数即
nd|ni=1ndi[gcd(i,nd)=1]

(后面的 i idd 化来的)

nd d 是一样的
所以就把式子弄成

nd|ni=1di[gcd(i,d)=1]

来看后面 di=1i[gcd(i,d)=1] 这串,其含义就是1到d中与d互质的数的和。而若有gcd(i,n)=1的话必有gcd(n-i,n)=1,即这些数都是成对出现的(除了1)。所以每对的和就是d,那么这些数的和就是 ϕ(d)d2 ,还要处理下1的,所以我就把式子写成了 ϕ(d)d+12 ,毕竟整数类型下除法去尾取整嘛。
又有两个积性函数相乘也是积性函数,所以 ϕ(d)d 是个积性函数。
于是就可以线性筛预处理 ϕ(d)d
你可以每次询问的时候在线算。但是 O(nlogn) 预处理答案会更快。
嗯..为什么是 O(nlogn) 呢?dalao说是均摊log的。
我代码里直接用 phi 表示 ϕ(d)d 了。

#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
#define N 1000100

bool ispri[N];
LL cnt,pri[N/4],phi[N],ans[N];
void Eular(LL lim)
{
    cnt=0;phi[1]=1;
    for (LL i=2;i<=lim;i++)
    {
        if (!ispri[i]) {pri[++cnt]=i;phi[i]=(i-1)*i;}
        for (LL j=1;j<=cnt && pri[j]*i<=lim;j++)
        {
            LL k=i*pri[j];
            ispri[k]=true;
            if (i%pri[j]==0)
            {
                phi[k]=pri[j]*phi[i]/i*k;
                break;
            }
            phi[k]=(pri[j]-1)*phi[i]/i*k;
        }
    }
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    LL T,lim,i,j,n;
    scanf("%lld",&T);
    lim=1000000;Eular(lim);
    for (i=1;i<=lim;i++) ans[i]=0;
    for (i=1;i<=lim;i++)
    {
        LL sm=(phi[i]+1)/2;
        for (j=i;j<=lim;j+=i) ans[j]+=sm;
    }
    while (T--)
    {
        scanf("%lld",&n);
        printf("%lld\n",n*ans[n]);
    }
    return 0;
}

你可能感兴趣的:(数论)