https://www.lydsy.com/JudgeOnline/problem.php?id=2818
给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的
数对(x,y)有多少对.
Input
一个整数N
Output
如题
Sample Input
4
Sample Output
4
Hint
hint
对于样例(2,2),(2,4),(3,3),(4,2)
1<=N<=10^7
题目思路:若Gcd(x,y)的结果为一个素数,我们不妨设该素数为p,那么Gcd(x/p,y/p)=1,也就是求在区间[1,n/p]内的互质的有序数对的数量,我们考虑欧拉函数φ(n)的定义就是小于n的且与n互质的正整数的个数,那么我们用一个前缀和数组来存储欧拉函数的和,在区间[1,n/p]的数量就是:sum[n/p]*2-1,减去1是因为(1,1)这一对算了两次。这只是一个素数p所贡献的数量,我们要统计在区间[1,n]的所有素数的贡献和。(有点绕 所以做这道题我也理解了好久才懂,举个例子说明吧)
phi[1] | (1,1) | |||
phi[2] | (1,2) | |||
phi[3] | (1,3) | (2,3) | ||
phi[4] | (1,4) | (3,4) | ||
phi[5] | (1,5) | (2,5) | (3,5) | (4,5) |
这样应该理解为什么要乘2减1了吧。
#include<iostream>
#include<cstdio>
#include<stack>
#include<cmath>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<iterator>
#define INF 0x3f3f3f3f
#define EPS 1e-10
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int prime[10000005];
int flag[10000005];
ll phi[10000005];
ll sum[10000005];
int num=0;
void euler(int n);
int main()
{
int n;
scanf("%d",&n);
euler(n);
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+phi[i];
ll re=0;
for(int i=1;i<=num;i++)
re+=sum[n/prime[i]]*2-1;
printf("%lld\n",re);
return 0;
}
void euler(int n)
{
phi[1]=1;//1要特判
for (int i=2;i<=n;i++)
{
if (flag[i]==0)//这代表i是质数
{
prime[++num]=i;
phi[i]=i-1;
}
for (int j=1;j<=num&&prime[j]*i<=n;j++)//经典的欧拉筛写法
{
flag[i*prime[j]]=1;//先把这个合数标记掉
if (i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];//若prime[j]是i的质因子,则根据计算公式,i已经
//包括i*prime[j]的所有质因子
break;//经典欧拉筛的核心语句,这样能保证每个数只会被自己最小的因子筛掉一次
}
else
phi[i*prime[j]]=phi[i]*phi[prime[j]];//利用了欧拉函数是个积性函数的性质
}
}
}