其实这题我也没太明白。。。
我们要求
[sum_{i=1}^{N-1}sum_{j=i+1}^Ngcd(i,j)
]
引理:
我们要求(gcd(i,j)=k)的个数,可转化为求(gcd(i/k,j/k)=1)的个数,即(varphi(N/k))。
那么如果要求所有满足(gcd(i,j)=k)的和,即求(varphi(N/k)*k)。
为了满足10000组询问的复杂度,我们需要对这个式子做一些手脚。
设(f(n)=gcd(1,n)+gcd(2,n)+cdots+gcd(n-1,n))
则最终答案(ans(n)=f(2)+f(3)+cdots+f(n))。
难点在如何求(f(n)),前面提到,对于一个(gcd(i,n)=k),它对(f(n))的贡献是(varphi(n/k)*k)。
于是如果我们枚举(k),把所有(f(n))算出来,复杂度就到了(O(n^2)),显然不可行。
然而实际上,对于一个(gcd(i,n)=k),有(kmid n),显然所有(gcd(i,n))一定不会超过(n),那么
[f(n)=sum_{kmid n}k*varphi(n/k)
]
该式含义可转化为,对于一个(k),他会对所有(f(ak),a>1)产生(k*varphi(ak/k))的贡献。于是我们枚举(k=1sim n),每次统计一遍(k)的贡献即可。
求完(f(n))后,计算(ans(n)),实际上就相当于算了一个前缀和,询问的时候直接输出就得了。
参考代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 4000010
#define MOD 2520
#define E 1e-12
#define ll long long
using namespace std;
inline int read()
{
int f=1,x=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
ll n,phi[N],p[N],cnt,v[N],sum[N],s[N];
inline void init(int n)
{
phi[1]=1;
for(int i=2;i<=n;++i){
if(!v[i]){phi[i]=i-1;p[++cnt]=i;}
for(int j=1;j<=cnt;++j){
if(p[j]>n/i) break;
v[i*p[j]]=1;
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];break;
}
phi[i*p[j]]=phi[i]*(p[j]-1);
}
}
for(int i=1;i<n;++i)
for(int j=i+i;j<n;j+=i){
sum[j]+=i*phi[j/i];
}
for(int i=1;i<=n;++i) sum[i]+=sum[i-1];
}
int main()
{
init(N);
while(~scanf("%d",&n)&&n!=0){
cout<<sum[n]<<endl;
}
return 0;
}