【欧拉函数应用——求gcd的和】
前言
欧拉函数可以用来求解gcd的和
P1390 公约数的和
题目描述
给定 \(n\),求
\(\sum_{i = 1}^n \sum_{j = i + 1}^n \gcd(i, j)\)
设\(f(j)=\sum_{i=1}^{j-1}gcd(i,j)\),则\(ans=\sum_{j=2}^nf(j)\)
\(f(j)=\sum\limits_{d|j}d*(\sum\limits_{gcd(i,j)=d,d<=i<j}1)\)
\(f(j)=\sum\limits_{d|j}d*(\sum\limits_{gcd(i/d,j/d)=1,1<=i/d<j/d}1)\)
\(f(j)=\sum\limits_{d|j}d*[j!=d]\phi(j/d)\)
那么我们可以通过倍数法\(O(nlog n)\)求出\(f\),再对\(f\)做一遍前缀和,就可以\(O(1)\)回答询问。
若是只有一个询问,可以对phi数组做一个前缀和,在通过倍数法求f的过程中,不记录f每个位置具体的值,而是对于枚举的每个d,算出其对于整个答案的贡献(即\(d*\sum_{i=2}^{\lfloor n/d\rfloor}\phi(i)\))。这样做就是线性的。
总结:
求gcd的和,考虑枚举gcd的所有可能取值\(d\),转化成\(d*gcd为d的个数\),再利用\(gcd(i,j)=d<=>gcd(i/d,j/d)=1\),转化成互质的关系,从而用欧拉函数解决。
通常可以设一个\(f(i)\),表示\(j\in[1,i]\)的\(\sum gcd(j,i)\),如果只有一次询问可以线性做,多次询问带一个log。
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
register int x=0,w=1;
register char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar(),w=-1;
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=~(x-1);
if(x>9) write(x/10);
putchar('0'+x%10);
}
const int N=2e6+100;
int n,phi[N],sum,v[N],p[N],cnt,s[N];
void init()
{
for(int i=2;i<=n;++i)
{
if(v[i]==0) p[++cnt]=i,phi[i]=i-1;
for(int j=1;p[j]*i<=n;++j)
{
v[p[j]*i]=1;if(i%p[j]==0) phi[i*p[j]]=phi[i]*p[j];
else phi[i*p[j]]=phi[i]*(p[j]-1);
if(i%p[j]==0) break;
}
s[i]=s[i-1]+phi[i];
}
}
signed main()
{
n=read();
init();
for(int i=1;i<=n;++i){
sum+=i*s[n/i];
}
write(sum);
return 0;
}

浙公网安备 33010602011771号