51nod 1237 最大公约数之和 V3【欧拉函数||莫比乌斯反演+杜教筛】
用mu写lcm那道卡常卡成狗(然而最后也没卡过去,于是写一下gcd冷静一下
首先推一下式子
\[\sum_{i=1}^{n}\sum_{j=1}^{n}gcd(i,j)
\]
\[\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{d=1}^{n}[gcd(i,j)==d]d
\]
\[\sum_{d=1}^{n}d\sum_{i=1}^{n}\sum_{j=1}^{n}[gcd(i,j)==d]
\]
\[\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{d} \right \rfloor}[gcd(i,j)==1]
\]
然后可以向两个方向推:莫比乌斯或者欧拉
首先推欧拉函数的:
为什么转成乘二加一的形式?考虑矩阵。\( \sum_{i=1}{n}\sum_{j=1}[gcd(i,j)1] \)的形式相当于把除了\( ij \)的数对\( (i,j) \)都算了两遍,所以乘二,这时只用算一遍的\( ij \)的数对也被算了两遍,这些数对中对答案有贡献的只有\( gcd(1,1)1 \)所以减去一
\[\sum_{d=1}^{n}d(2*\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\phi(i)-1)
\]
\[2*\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\phi(i)-\sum_{i=1}^{n}i
\]
转成这种形式就可以分块+杜教筛做了
拒绝算时间复杂度(。
然后莫比乌斯反演(不想卡常所以没写代码,最后应该带个ln:
\[\sum_{d=1}^{n}d\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{k|gcd(i,j)}\mu(k)
\]
\[\sum_{d=1}^{n}d\sum_{k=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\mu(k)\left \lfloor \frac{n}{dk} \right \rfloor^2
\]
欧拉函数的代码,因为时间很充足,所以为了方便全部用了long long
#include<iostream>
#include<cstdio>
using namespace std;
const long long N=1000005,m=1000000,mod=1e9+7,inv2=500000004;
long long n,ans,q[N],tot,phi[N],ha[N];
bool v[N];
long long wk(long long x)
{
if(x>=mod)
x-=mod;
return x%mod*(x+1)%mod*inv2%mod;
}
long long slv(long long x)
{
if(x<=m)
return phi[x];
if(ha[n/x])
return ha[n/x];
long long re=wk(x);
for(long long i=2,la;i<=x;i=la+1)
{
la=x/(x/i);
re=(re-(la-i+1)%mod*slv(x/i)%mod)%mod;
}
return ha[n/x]=re;
}
int main()
{
phi[1]=1;
for(long long i=2;i<=m;i++)
{
if(!v[i])
{
q[++tot]=i;
phi[i]=i-1;
}
for(long long j=1;j<=tot&&q[j]%mod*i<=m;j++)
{
long long k=i%mod*q[j];
v[k]=1;
if(i%q[j]==0)
{
phi[k]=phi[i]%mod*q[j];
break;
}
phi[k]=phi[i]%mod*(q[j]-1);
}
}
for(long long i=1;i<=m;i++)
phi[i]=(phi[i]+phi[i-1])%mod;
scanf("%lld",&n);
for(long long i=1,la;i<=n;i=la+1)
{
la=n/(n/i);
ans=(ans+(wk(la)-wk(i-1))%mod*slv(n/i)%mod)%mod;
}
printf("%lld\n",((ans%mod*2-wk(n))%mod+mod)%mod);
return 0;
}