[BZOJ]2226: [Spoj 5971] LCMSum 欧拉函数(或莫比乌斯反演)

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.

题解:

做法一:
有这个结论就很简单了:小于等于n且与n互质的数的和,然后就可以 O(nlogn) 预处理出所有的答案。( n+n2+n3....+1 )是 O(nlogn) 的。
做法二:
莫比乌斯反演。
大力推式子:

ansn=i=1nlcm(n,i)=i=1nn×igcd(n,i)
枚举最大公约数 d ,设 F(x) <=x 且与 x 互质的数的和,很容易推出以下式子:
ansn=d|nF(nd)×n
然后考虑如何求 F(x)
F(x)=i=1x[gcd(x,i)==1?1:0]×i
然后就是莫比乌斯反演的基本变形:
F(x)=i=1xi×d|gcd(x,i)μ(d)
F(x)=i=1xi×d|xd|iμ(d)
然后将式子变为枚举 d ,考虑每个 μ(d) 对答案的贡献:
F(x)=d|xμ(d)×sum(d,xd×d,d)
上面的 sum(l,r,c) 表示首项为 l ,末项为 r ,公差为 c 的等差数列的和。
然后就可以通过枚举因数来预处理出 F ans ,复杂度也是 O(nlogn) 的,但是多了点常数,竟然是20s卡过的……

代码(做法一):

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=1000010;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int prime[Maxn>>1],len=0;
LL phi[Maxn],ans[Maxn],sum[Maxn];
bool mark[Maxn];
void pre()
{
    memset(mark,false,sizeof(mark));
    phi[1]=1;
    for(int i=2;i<=1000000;i++)
    {
        if(!mark[i]){prime[++len]=i;phi[i]=i-1;}
        for(int j=1;j<=len&&prime[j]*i<=1000000;j++)
        {
            mark[prime[j]*i]=true;
            if(i%prime[j]==0)
            {
                phi[prime[j]*i]=prime[j]*phi[i];
                break;
            }
            phi[prime[j]*i]=(prime[j]-1)*phi[i];
        }
    }
    sum[1]=1;
    for(int i=2;i<=1000000;i++)sum[i]=phi[i]*(LL)(i)/2LL;
    for(int i=1;i<=1000000;i++)
    for(int j=i;j<=1000000;j+=i)
    ans[j]+=sum[i];
    for(int i=1;i<=1000000;i++)ans[i]*=(LL)(i);
}
int main()
{
    pre();
    int T=read();
    while(T--)printf("%lld\n",ans[read()]);
}

代码(做法二):

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=1e6+10;
const int inf=1e6;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int prime[Maxn],len=0,mu[Maxn];
bool mark[Maxn];
LL F[Maxn],ans[Maxn];
LL Sum(LL l,LL r,LL c)
{
    LL x=(r-l)/c+1;
    return (l+r)*x/2LL;
}
void pre()
{
    memset(F,0,sizeof(F));
    memset(ans,0,sizeof(ans));
    memset(mark,false,sizeof(mark));
    mu[1]=1;
    for(int i=2;i<=inf;i++)
    {
        if(!mark[i])prime[++len]=i,mu[i]=-1;
        for(int j=1;j<=len&&prime[j]*i<=inf;j++)
        {
            mark[prime[j]*i]=true;
            if(i%prime[j]==0){mu[prime[j]*i]=0;break;}
            mu[prime[j]*i]=-mu[i];
        }
    }
    for(int d=1;d<=inf;d++)
    for(int x=d;x<=inf;x+=d)
    F[x]+=(LL)(mu[d]*Sum(d,(x/d)*d,d));
    for(int d=1;d<=inf;d++)
    for(int x=d;x<=inf;x+=d)
    ans[x]+=F[x/d]*(LL)(x);
}
int main()
{
    pre();
    int T=read();
    while(T--)printf("%lld\n",ans[read()]);
}

你可能感兴趣的:(数论,莫比乌斯反演)