luoguP2568GCD【线性筛法求欧拉函数】

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

输入格式
一个整数N

输出格式
答案

输入输出样例
输入 #1复制
4
输出 #1复制
4
说明/提示
对于样例(2,2),(2,4),(3,3),(4,2)

1<=N<=10^7

来源:bzoj2818

本题数据为洛谷自造数据,使用CYaRon耗时5分钟完成数据制作。

由题意,假设(x<=y)
g c d ( x , y ) = p ( p 为 质 数 ) gcd(x,y)=p(p为质数) gcd(x,y)=p(p)
那么
g c d ( x p , y p ) = 1 ( p 为 质 数 ) gcd(\frac{x}{p},\frac{y}{p})=1(p为质数) gcd(px,py)=1(p)
考虑y对答案的贡献,可以得到是在求 y p \frac{y}{p} py的欧拉函数,那么我们用线性筛欧拉函数得到即可。
但是注意,p可以为很多值,如果直接暴力计算,会TLE。
仔细观察会发现 y p \frac{y}{p} py对答案的贡献= k × φ ( y p ) k×φ(\frac{y}{p}) k×φ(py),其中k满足 ( p r i m e [ k + 1 ] × y p > n ) 且 ( p r i m e [ k ] × y p < = n ) (prime[k+1]×\frac{y}{p}>n)且(prime[k]×\frac{y}{p}<=n) (prime[k+1]×py>n)(prime[k]×py<=n)
所以我们换一种思路,考虑数 i ( 1 ≤ i ≤ n ) i(1≤i≤n) i(1in)对答案的贡献。
由于质数表里的质数和 i i i都是递增的,所以不用二分查找来实现,直接用一个指针 j j j,最开始指着质数表最后一个,每次一直将 j j j减到 i × p r i m e [ j ] < = n i×prime[j]<=n i×prime[j]<=n为止,在直接用上式计算答案。
由唯一分解定理可以得到无重复计算答案。
复杂度显然为O(n)。
注意中间有点细节,具体看代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e7+10;
int n;
bool mark[N];
int prime[N];
ll phi[N];
ll ans;
int cnt;
int main()
{
	scanf("%d",&n);
	phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!mark[i]) prime[++cnt]=i,phi[i]=i-1;
		for(int j=1;j<=cnt&&prime[j]*i<=n;j++)
		{
			mark[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]*(prime[j]-1);
		}
	}
	int j=cnt;
	for(int i=1;i<=n;i++)
	{
		while(prime[j]*i>n) j--;
		if(i==1)//当i等于1的时候可以发现x等于y,此时只用计算一次
		ans+=phi[i]*j; 
		else ans+=((phi[i]*j)<<1);//不等于的话可以交换x和y得到另一组答案
		//故要乘以2
	}
	printf("%lld",ans);
	return 0;
}

谢谢

posted @ 2019-12-29 11:48  最爱丁珰  阅读(31)  评论(0编辑  收藏  举报