(luogu2568)GCD

原题链接

思路:

首先,显然当 \(p\) 为质数时,\((p,p)\) 是质数.

同时,若 \(a > b\) \((a,b) = g \neq 1\) 时,有 \(a = cg, b = dg, c > d\) \((c,d)=1\)

此时答案已经呼之欲出了:从 \(c\) 入手, 计算出 \(\phi(c)\) 即可知有多少 \(d < c\) 满足 \((c,d)=1\), 然后计算出 \(\lfloor \frac{n}{c} \rfloor\) 内有多少质数, 再把两数相乘,即得到满足 \(n \geq a > b\) \(g = (a,b)\) 为质数的数对数量了.

而这两个数可以通过线性筛在 \(O(n)\) 的时间内预处理得到。问题便解决了.

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int Mn(10000500);

int phi[Mn],lsp[Mn]; //phi函数, 1~i的质数个数
vector<int> prm; //1~n内的质数
void euler_sieve(int n) { //线性筛
phi[1] = 0;
lsp[1] = 0;
for(int i(2);i<=n;++i) {
lsp[i] = lsp[i-1];
if(!phi[i]) { //若i为质数
phi[i] = i-1;
prm.push_back(i);
++lsp[i];
}
for(int j(0);j<prm.size() && 1ll*i*prm[j]<=n;++j) {
if(i%prm[j]==0) { //保证每个数只被筛一次
phi[i*prm[j]] = phi[i]*prm[j];
break;
} else {
phi[i*prm[j]] = phi[i]*(prm[j]-1);
}
}
}
}

int main() {
int n;
scanf("%d",&n);
euler_sieve(n); //预处理
long long ans(0);
for(int i(2);i<=n;++i) {
if(phi[i]==i-1) { //如果i为质数则(i,i)为一个答案
ans += 1;
}
ans += 2*phi[i]*lsp[n/i]; //(a,b)与(b,a)都是答案, 所以*2
}
printf("%lld",ans);
return 0;
}