隐藏
「NOIP十连赛day8」神炎皇 - 数论+线性筛 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「NOIP十连赛day8」神炎皇 - 数论+线性筛

题目大意

对于一个整数对$(a,b)$,若满足$a+b\le n$且$a+b$是$ab$的因子,则成为神奇的数对。请问这样的数对共有多少呢?


题目分析

设$\gcd(a,b)=gcd$。
设$\frac{a}{gcd}=x,\frac{b}{gcd}=y$,
$\because a+b\mid ab$
$\therefore \gcd(x+y)\mid gcd^2xy$
$\because \gcd(x,y)=1$
$\therefore x+y\mid gcd$

题目问题转化为,统计$x+y\le\frac{n}{gcd}$,$x+y\mid gcd$的个数。

令$x+y=k$。
当$k$一定时,$x+y=k,\gcd(x,y)=1$的个数为$\phi(k)$。
题目问题对$k$的限制过多,尝试将枚举$gcd$转化为枚举$k$。
故问题转化为:统计$gcd\le \frac n{k},gcd$是$k$的倍数的个数。

显然这样的$k$会出现$\frac n{k^2}$次。

故$Ans=\frac{n}{k^2}\phi(k)$


代码

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
44
45
46
47
48
49
50
51
52
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
const int maxn=10000005;
LL n,Phi[maxn],Prime[maxn],ans=0;
int cnt=0;
bool vst[maxn];
void Phi_Table(int n) { //欧拉函数线性筛
Phi[1]=1;
for(int i=2; i<=n; i++) {
if(!vst[i]) {
Prime[++cnt]=i;
Phi[i]=i-1;
}
for(int j=1; j<=cnt&&Prime[j]*i<=n; j++) {
vst[Prime[j]*i]=1;
if(i%Prime[j]==0) {
Phi[i*Prime[j]]=Phi[i]*Prime[j];
break;
}
Phi[i*Prime[j]]=Phi[i]*Phi[Prime[j]];
}
}
}
int main() {
n=Get_Int();
Phi_Table(sqrt(n)+5);
for(int i=2; i<=sqrt(n); i++)ans+=n/i/i*Phi[i];
printf("%lld\n",ans);
return 0;
}
姥爷们赏瓶冰阔落吧~