GCD - Extreme (II) UVA - 11426

吃可爱长大的小学妹 提交于 2019-11-30 00:27:50

题意:输入正整数n , 求gcd(1 ,2)+gcd(1 ,3)+gcd(2,3)+. . . +gcd(n-1,n)时,即所有满足1 <= i <j<=n 的数对(i,j)所对应的gcd(i,j)之和。比如n= l0 时答案为67 , n=100 时答案为13 015 , n=200 000时答案为143295493 160 。

设f(n)=gcd(1 ,n)+gcd(2 ,n)+gcd(3 ,n)+…+gcd(n-1, ,n),
所求答案为S(n)=f(2)+f(3)+…f(n) 。
只需求出f(n) , 就可以递推出所有答案: S(n)=S(n-1)+f(n) 。
注意到所有gcd(x,n)的值都是n 的约数,可以按照这个约数进行分类,用g(n, i)表示满足gcd(x,n)=i 且x<n 的正整数x 的个数,则f(n)=sum{i*g(n, i) | i 是n 的约数}。
注意到gcd(x,n)=i的充要条件是gcd(x/i,n/i)=1(此时x/i和n/i互质), 因此满足条件的x/i 有phi(n/i)个,说明g(n , i)=phi(n/i) 。
问题到这里还没有结束。如果依次计算j例,需要对每个n 枚举它的约数i , 速度较慢,但如果把思路逆转过来,对于每个i 枚举它的倍数n (并且更新j(n) 的值) ,时间复杂度将降为与素数筛法同阶。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define ll long long
#define maxn 4000010
bool flag[maxn];
int phi[maxn],prime[maxn];
ll s[maxn],f[maxn];
int num=0;
void euler()
{
	phi[1]=1;
	for(int i=2;i<=maxn;i++)
	{
		if(!flag[i])
		{
			prime[++num]=i;
			phi[i]=i-1;
			
		}
		for(int j=1;j<=num&&(ll)prime[j]*i<=maxn;j++)
		{
			flag[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			else phi[i*prime[j]]=phi[i]*phi[prime[j]];
		}
	}
 } 
 int main()
 {
 	euler();
 	for(int i=1;i<maxn;i++)
 	{
 		for(int n=i*2;n<maxn;n+=i)
 		{
 			f[n]+=i*phi[n/i];
		 }
	 }
	 s[2]=f[2];
	 for(int n=3;n<maxn;n++)
	 {
	 	s[n]=s[n-1]+f[n];
	  } 
	  int n;
	  while(scanf("%d",&n)!=EOF&&n)
	  {
	  	printf("%lld\n",s[n]);
	  }
 }
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!