BZOJ 2818: 欧拉筛法求gcd(x,y)==k(k为素数)详解

给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的 
数对(x,y)有多少对.

gcd(x,y)=p
gcd(x/p,y/p)=1
枚举每个素数p,计算1~n/p中有多少对互质的数
表示1~i中有多少个与i互质的数,即phi(i)
g[i]表示f[i]的前缀和
ans=2*∑g[n/p]-cnt
tot是n以内素数的个数

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<iostream>
#define maxn 10000100

using namespace std;
//gcd(x,y)=p
//gcd(x/p,y/p)=1
///枚举每个素数p,计算1~n/p中有多少个互质的数 即有多少个x
int prime[maxn],phi[maxn];//素数表  欧拉表
//素数表是第几个素数是什么,欧拉表是i的欧拉数是phi[i];
bool vis[maxn];//用于打表记录的中间量
long long g[maxn];//g[i] 前i个的欧拉函数和
long long ans;
int n,tot;//素数个数

int main()
{
	scanf("%d",&n);
	phi[1]=1;g[1]=1;
	for (int i=2;i<=n;i++) //素数+欧拉表
	{
		if (!vis[i])
		{
			prime[++tot]=i; phi[i]=i-1;//因为质数i除了1以外的因数只有i,故1至i的欧拉数为i-1个
		}
		for (int j=1;j<=tot && prime[j]*i<=n;j++)
		{
			vis[prime[j]*i]=1;//素数筛
			if (i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];//对于i%pris[j]==0的项,则说明pris[j]已经在 
 //i中已经出现了,而且是最小的,观察phi函数的计算式,可知此时应乘pris[j]
				break;
			}
			else phi[i*prime[j]]=phi[i]*(prime[j]-1);   
//对于i%pris[j]!=0的项,它的最小质因子一定是pris[j],则由积性函数性质可得 
  // phi[i*prime[j]]=phi[i]*phi[pris[j]],其中phi[pris[j]]=pris[j]-1 
		}
		g[i]=(long long)g[i-1]+phi[i];// 前i 的欧拉函数和
	}
	for (int i=1;i<=tot;i++) ans+=g[n/prime[i]];
	printf("%lld\n",ans*2-tot);//tot是n以内素数的个数 减去(x,x)这种情况
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41668093/article/details/83756149