GCD(洛谷 2568)
题目描述
给定整数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
由题意得:gcd(x,y)=p(这里我们假设p为一个已知的质数),并且下面的过程都在这个条件下进行,我们不妨设x<=y<=n。
令x=a*p,y=b*p,则有gcd(a,b)=1,且1<=a,b<=n/p。同样不妨设a<=b(☚什么情况下会等于后面有讲到)。
那么满足gcd(x,y)=p的数对(x,y)个数其实就是满足gcd(a,b)=1的数对个数(a,b)。(下面用num(x,y)来表示满足gcd(x,y)=p的数对(x,y)个数,num(a,b)同理)
对于b来说,b的取值范围是1~n/p,而a的取值可以是φ(b)中的任意一个,所以num(x,y)=num(a,b)=∑φ(b)(b∈[1,n/p]),简单来说,就是1~n/p中所有数的欧拉函数值的和
好,代码已经成功一大半了,但是现在我们并没有p的准确值,p可能是1~n中的任意一个质数。所以我们可以在预处理中先把1~n中的质数(p1、p2……)全部找出来,最后一一枚举这些质数,每枚举一个p,就处理 当gcd(x,y)等于此时的p 的时候的num(x,y),加到最后输出的结果ans中
代码实现:首先,我们用线性筛法求出1~n中每个数的欧拉函数值,用一个数组sum(注意要开long long)来存欧拉函数值的前缀和,方便后面查找值。
其实可以这样理解,sum[i]存的就是1~i中所有数的欧拉函数值的和,也就是上面讲到的num(x,y)
那么最后枚举1~n中的质数,每一次循环都得到了一个n/p值,即b的上界,那么此时的sum[n/p]*2-1就是满足gcd(x,y)=p的(x,y)的对数。
解释一下:由样例可得,(2,4)和(4,2)是不同的两种答案,所以x,y可以交换位置得到一个新的答案,所以sum[n/p]要*2,;但是当a=b=1,即x=y=p时,只有一种答案,所以sum[n/p]*2需要再-1,将重复的删去。
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int N=10000005; 5 int n; 6 ll sum[N],vis[N],p[N],phi[N]; 7 int main() 8 { 9 scanf("%d",&n); 10 memset(vis,1,sizeof(vis)); 11 vis[0]=vis[1]=false; 12 phi[1]=1; 13 for(int i=2;i<=n;i++) 14 { 15 if(vis[i])p[++p[0]]=i,phi[i]=i-1;//记得找出质数 16 for(int j=1;j<=p[0]&&p[j]*i<=n;j++) 17 { 18 vis[p[j]*i]=false; 19 if(i%p[j])phi[i*p[j]]=phi[p[j]]*phi[i]; 20 else 21 { 22 phi[i*p[j]]=p[j]*phi[i]; 23 break; 24 } 25 }//线性筛法 26 } 27 for(int i=1;i<=n;i++)sum[i]=sum[i-1]+phi[i];//前缀和 28 ll ans=0; 29 for(int i=1;i<=p[0]&&p[i]<=n;i++)//枚举1~n中的所有质数 30 ans+=(sum[n/p[i]]<<1)-1;//sum[n/p]*2-1 31 printf("%lld",ans); 32 return 0; 33 }