最大公因数(gcd.c/.cpp/.pas)

题目描述

    给定正整数n,求。

样例输入

6

样例输出

15

数据范围

对于40%的数据:1<=n<=1000000

对于100%的数据:1<=n<=3*10^12

 

提示:保证答案不超过10^18

原创题!

……好吧其实最后发现bzoj2705【longge的问题】跟这个一毛一样,但是这个数据更强

不过我真是独立yy出来的

首先,考虑gcd(i,n)==k的i有几个

显然这是等价于gcd(j,n/k)==1的j有几个,其中1<=j<=n/k

看到gcd==1,即j与n/k互质,你想到什么?显然是欧拉函数啊

gcd(j,n/k)==1的j的个数就是phi(n/k),其中每个gcd(i,n)==k对答案的贡献是k,所以总的答案就是Σphi(n/k)*k,1<=k<=n且n%k==0

显然这个式子等价于Σphi(k)*(n/k),1<=k<=n且n%k==0

那么对n分解质因数,然后枚举它的每个因子取了多少个,算phi(k)*(n/k)就近似O(1)了

总复杂度是O(sqrt(n))

(所以完全可以开到10^15的,只是怕爆了long long要高精太麻烦)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<deque>
#include<set>
#include<map>
#include<ctime>
#define LL long long
#define inf 0x7ffffff
#define pa pair<int,int>
#define mxprime 1500000
using namespace std;
bool mrk[1500010];
LL prime[1000000],a[100];
LL n,wrk,ans;
int lenp,len;
int b[100],now[100];
inline LL read()
{
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void getprime()
{
	for (int i=2;i<=mxprime;i++)
	  mrk[i]=1;
	for (int i=2;i<=mxprime;i++)
	  if (mrk[i])
	  {
	  	prime[++lenp]=(LL)i;
	  	for(int j=2*i;j<=mxprime;j+=i)mrk[j]=0;
	  }
}
inline void dfs(int step)
{
	if (step==len+1)
	{
		LL sum=1ll,tot=1ll;
		for (int i=1;i<=len;i++)
		{
		  for (int j=1;j<=now[i];j++)
		    sum*=a[i],tot*=a[i];
		  if (now[i])sum=sum/a[i]*(a[i]-1);
		}
		ans+=sum*(n/tot);
		return;
	}
	for(int j=0;j<=b[step];j++)
	{
		now[step]=j;
		dfs(step+1);
	}
}
int main()
{
	getprime();
	n=wrk=read();
	for (int i=1;prime[i]*prime[i]<=wrk;i++)
	{
		LL nowp=prime[i];
		if (wrk%nowp==0)
		{
			a[++len]=nowp;
			b[len]=1;
			wrk/=nowp;
			while (wrk%nowp==0)
			{
				b[len]++;
				wrk/=nowp;
			}
		}
	}
	if (wrk!=1)
	{
		if (wrk==a[len])b[len]++;
		else
		{
			a[++len]=wrk;
			b[len]=1;
		}
	}
	dfs(1);
	printf("%lld\n",ans);
}