题目链接:传送门
题目:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
题目描述 给定整数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,因为gcd(x, y)为素数,令p = gcd(x, y),a = x/p,b = y/p,则gcd(a, b) = 1;
所以对于给定的素数p,只要a,b满足gcd(a, b) = 1,则对应的x = a*p,y = b*p就是满足题意的一个数对。因为gcd(a, b) = 1,考虑欧拉函数。
不妨令a ≤ b,则对于一个确定的b,a的选择数有φ(b)种,那么所有a,b的选择就有$sum phi (b)$种,b*p ≤ N,所以b的上界为N/p。a,b可以互换位置所以要×2,a,b相等时(a = b = 1)只能算一种所以要-1,所以答案为$sum_{primeleqslant N}left ( 2*sum_{j=1}^{N/prime}phi (j)-1 ight )$。
线性筛出质数和欧拉函数O(N),枚举所有小于N的素数就可以了O(N/logN)。
总复杂度O(N+N/logN)。
注意:1e7的数组开多了会MLE,而最小的质数为2,所以欧拉函数和前缀和可以只开到N/2的大小。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int MAX_N = 1e7 + 5; int N; int prime[MAX_N+1]; int phi[MAX_N/2+1]; ll sum[MAX_N/2+1]; void getPrime_and_Phi() { memset(prime, 0, sizeof prime); phi[1] = 1; for (int i = 2; i <= MAX_N; i++) { if (!prime[i]) prime[++prime[0]] = i, phi[i] = i-1; for (int j = 1; j <= prime[0] && prime[j] <= MAX_N/i; j++) { prime[prime[j]*i] = 1; if (prime[j]*i <= MAX_N/2) phi[prime[j]*i] = phi[i] * (i%prime[j] ? prime[j]-1 : prime[j]); if (i % prime[j] == 0) break; } } } void init() { getPrime_and_Phi(); sum[0] = 0; for (int i = 1; i <= MAX_N/2; i++) { sum[i] = sum[i-1] + phi[i]; } } int main() { init(); cin >> N; ll ans = 0; for (int i = 1; i <= prime[0] && prime[i] <= N; i++) { ans += sum[N/prime[i]]*2-1; } cout << ans << endl; return 0; }