洛谷P2398

题目描述

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;
}

猜你喜欢

转载自blog.csdn.net/Amuseir/article/details/81591486