【CodeForces】915 G. Coprime Arrays 莫比乌斯反演
【题意】当含n个数字的数组的总gcd=1时认为这个数组互质。给定n和k,求所有sum(i),i=1~k,其中sum(i)为n个数字的数组,每个数字均<=i,总gcd=1的方案数。n<=2*10^6。答案将所有sum(i)处理成一个数字后输出。
【算法】数论(莫比乌斯反演)
【题解】假设当前求sum(k),令f(i)表示gcd=i的数组方案数,F(i)表示i|gcd的数组的方案数。
因为F(x)=Σx|df(d),由莫比乌斯反演定理,f(x)=Σx|dμ(d/x)*F(d)。
又F(x)=(k/x)^n,所以f(1)=Σμ(d)*(k/d)^n,d=1~k。
初始k=1,当k++时ans+=Σd|kμ(d)*((k/d)^n-(k/d-1)^n),这个过程只需要k ln k枚举贡献答案即可,同时预处理快速幂。
复杂度O(k log n+k ln k)。
#include<cstdio> const int N=2000010,MOD=1e9+7; int n,m,miu[N],prime[N],mark[N],sum[N],p[N],tot,ans,ANS; int pow(int x,int k){ if(!x)return 0; int ans=1; while(k){ if(k&1)ans=1ll*ans*x%MOD; x=1ll*x*x%MOD; k>>=1; } return ans; } int main(){ scanf("%d%d",&n,&m); miu[1]=1; for(int i=2;i<=m;i++){ if(!mark[i]){miu[prime[++tot]=i]=-1;} for(int j=1;j<=tot&&i*prime[j]<=m;j++){ mark[i*prime[j]]=1; if(i%prime[j]==0)break; miu[i*prime[j]]=-miu[i]; } } for(int i=0;i<=2000000;i++)p[i]=pow(i,n); for(int i=1;i<=m;i++){ for(int j=i;j<=m;j+=i)sum[j]=((sum[j]+1ll*miu[i]*(p[j/i]-p[j/i-1]))%MOD+MOD)%MOD; ans=(ans+sum[i])%MOD; ANS=(ANS+(ans^i))%MOD; } printf("%d",ANS); return 0; }