分类讨论
对于满足要求的数对(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;
}