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*py=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 }
View Code

写这篇题解的时候思维比较混乱,我都不知道该如何组织语言了,花了超长时间的!希望能让你理解吧OvO

如果可以的话,给个“推荐”资瓷一下吧(*^▽^*)

//参考:zhou_yk 的博客

原文地址:https://www.cnblogs.com/ljy-endl/p/11409675.html