题目描述
for i=1 to n
for j=1 to n
sum+=gcd(i,j)
给出n求sum. gcd(x,y)表示x,y的最大公约数.
输入输出格式
输入格式:
n
输出格式:
sum
输入输出样例
输入样例:
2
输出样例:
5
由于直接枚举i,j的话实在是太大。我们可以考虑枚举其他的数
我们发现对于任意两个数来说,其gcd的出现次数是有限的,而且由于gcd的计算过程,我们可以将时间复杂度优化到logn级别
所以我们考虑枚举gcd,然后用gcd的值乘以其出现的次数来算出对答案的贡献
我们假设当前枚举到的gcd值为k,那么满足gcd(a,b)为k的情况当且仅当a=x*k,b=y*k,且x、y互质。
所以当我们枚举了k以后,再求出1到n中有多少对k的倍数是除了k之后互质的,然后再使用该对数来乘以k就是k对答案的贡献
我们已经知道,对于k来说,gcd(xk,yk)==1,那么gcd(x,y)==1,所以问题可以转换为枚举k以后,求出1,n/k中总共有多少对互质对。
而这个互质对的求法,我们可以直接使用欧拉函数
由于PHI(i)表示1~i中与i互质的数的个数,所以其个数*2-1就是1~i中与i有关的互质对对数,而我们要求互质对总共的个数,也就是将所有n/k以内的PHI求和,与也就是n/k内所有数的互质对个数。
那么我们直接用线性筛O(n)求出素数与前缀和,然后求出PHI的前缀和,枚举k以后相加即可
时间复杂度O(n)
#include<cstdio>
#include<vector>
#define LL long long
const int MAXN=1e7+5;
int prime[MAXN];
bool isnot[MAXN];
int phi[MAXN];
int ptot;
LL sum[MAXN];
void linear_seive(int n)
{
isnot[1]=1;
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(!isnot[i])
{
prime[++ptot]=i;
phi[i]=i-1;
}
for(int t=1;t<=ptot;t++)
{
int j=prime[t]*i;
if(j>n)
{
break;
}
isnot[j]=1;
phi[j]=phi[i]*phi[prime[t]];
if(!(i%prime[t]))
{
phi[j]=prime[t]*phi[i];
break;
}
}
}
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+1ll*phi[i];
}
}
int main()
{
int n;
std::scanf("%d",&n);
linear_seive(n);
LL ans=0;
for(int k=1;k<=n;k++)
{
LL num=sum[n/k]*2-1;
ans+=num*k;
}
std::printf("%lld\n",ans);
return 0;
}