Luogu P2568 GCD 题解

题目

分类讨论

对于满足要求的数对(x,y),有两种情况:

A:x、y 中有一个是质数p,另一个数是p的倍数,此时gcd(x, y)显然为p,满足要求

B:x = p·k1, y = p·k2, p为质数,且k1, k2互质,gcd(x, y)也为质数p

方案计算

先用素数线性筛和欧拉函数的递推式预处理出1——n的数中的质数和每个数的欧拉函数

对于情况
A:枚举每个质数,注意(x,y)和(y, x)不同,因此要乘上 2,但当x、y相同时只能算一种,因此乘上2再减去1

B:枚举1——n的每个数 i,x = p·k1, y = p·k2(p为质数),以i为k1的情况数有:(满足p·k1<=n且p<=i的质数p的个数) * (i之前与i互质的数的个数(欧拉函数)),注意也要乘上2

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 5;
int n, tot, k[N], p[N], a[N], s[N];
long long res;
void Pre_work () {
	for (int i = 2; i <= n; ++i) {
		if (!k[i]) p[++tot] = i, a[i] = i - 1;   //如果是质数,与 i 互质的数的个数为 i-1 
		for (int j = 1; j <= tot, p[j] * i <= n; ++j) {
			k[p[j] * i] = 1, a[p[j] * i] = a[i] * (p[j] - (i % p[j] != 0));  //套用欧拉函数递推式 
			if (i % p[j] == 0) break;
		}
	}
	for (int i = 2; i <= n; ++i) s[i] = s[i - 1] + (k[i] == 0);     //预处理质数个数的前缀和 
}
int main() {
	scanf ("%d", &n);
	Pre_work ();
	for (int i = 2; i <= n; ++i) res += s[n / i] * (a[i] - 1) << 1;   //情况 B  
	for (int i = 1; i <= tot and p[i] <= n; ++i) res += ((n / p[i]) << 1) - 1; //情况 A 
	printf ("%lld
", res);
	return 0;
} 
原文地址:https://www.cnblogs.com/whx666/p/11923723.html