UVA11424 GCD

其实这题我也没太明白。。。

我们要求

[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;
}
原文地址:https://www.cnblogs.com/DarkValkyrie/p/11755263.html