题意:
给定两个整数
N
N
N,
M
M
M,求有多少个整数
X
X
X(
1
≤
X
≤
N
1\leq X \leq N
1≤X≤N),满足
M
≤
g
c
d
(
X
,
N
)
M \leq gcd(X, N)
M≤gcd(X,N)
思路:
题意很简单,思路却有点难想。
首先
N
N
N是一个已知的整数,而
g
c
d
(
X
,
N
)
gcd(X,N)
gcd(X,N)一定是
N
N
N的一个约数
而
N
N
N的约数并不会很多,故我们可以逐一枚举,并找到每一个不小于
M
M
M的约数,此处设该约数为P
则:
P
∣
N
P|N
P∣N 且
M
≤
P
M \leq P
M≤P
而对于枚举出的每一个P,原问题得到了简化:
对于一个已知的整数P,求有多少个整数
X
X
X(
1
≤
X
≤
N
1\leq X \leq N
1≤X≤N),满足
g
c
d
(
X
,
N
)
=
P
gcd(X, N) = P
gcd(X,N)=P
想到这里,解法其实离我们已经很近了。
让我们设简化后的问题答案为
A
n
s
Ans
Ans:
则:
A
n
s
=
∑
i
=
1
N
[
g
c
d
(
i
,
N
)
=
=
P
]
Ans = \sum_{i=1}^{N}[gcd(i, N) == P]
Ans=i=1∑N[gcd(i,N)==P]
=
∑
i
=
1
⌊
N
P
⌋
[
g
c
d
(
i
∗
P
,
N
)
=
=
P
]
= \sum_{i=1}^{\lfloor\frac{N}{P}\rfloor} [gcd(i*P, N) == P]
=i=1∑⌊PN⌋[gcd(i∗P,N)==P]
=
∑
i
=
1
⌊
N
P
⌋
[
g
c
d
(
i
,
⌊
N
P
⌋
)
=
=
1
]
= \sum_{i=1}^{\lfloor\frac{N}{P}\rfloor} [gcd(i, \lfloor\frac{N}{P}\rfloor) == 1]
=i=1∑⌊PN⌋[gcd(i,⌊PN⌋)==1]
显然,上面的式子正是欧拉函数的定义式
即
φ
(
⌊
N
P
⌋
)
\varphi(\lfloor\frac{N}{P}\rfloor)
φ(⌊PN⌋)
对于不太理解欧拉函数的朋友,可以接着看下面的推导:
回到刚才的式子:
A
n
s
=
∑
i
=
1
⌊
N
P
⌋
[
g
c
d
(
i
,
⌊
N
P
⌋
)
=
=
1
]
Ans = \sum_{i=1}^{\lfloor\frac{N}{P}\rfloor} [gcd(i, \lfloor\frac{N}{P}\rfloor) == 1]
Ans=i=1∑⌊PN⌋[gcd(i,⌊PN⌋)==1]
基于容斥的思想,我们引入艾弗森约定:
∑
d
∣
n
μ
(
d
)
=
(
n
=
=
1
)
\sum_{d|n}\mu(d) = (n == 1)
d∣n∑μ(d)=(n==1)
假设:
T
=
⌊
N
P
⌋
T = \lfloor\frac{N}{P}\rfloor
T=⌊PN⌋
故原式可转化为:
A
n
s
=
∑
i
=
1
T
[
g
c
d
(
i
,
T
)
=
=
1
]
Ans = \sum_{i=1}^{T}[gcd(i,T)==1]
Ans=i=1∑T[gcd(i,T)==1]
=
∑
i
=
1
T
∑
d
∣
g
c
d
(
i
,
T
)
μ
(
d
)
=\sum_{i=1}^T\sum_{d|gcd(i,T)} \mu(d)
=i=1∑Td∣gcd(i,T)∑μ(d)
=
∑
d
∣
T
μ
(
d
)
∗
T
d
=\sum_{d|T} \mu(d)* \frac{T}{d}
=d∣T∑μ(d)∗dT
由公式:
∑
d
∣
T
μ
(
d
)
d
=
φ
(
T
)
T
\sum_{d|T}\frac{\mu(d)}{d} = \frac{\varphi(T)}{T}
d∣T∑dμ(d)=Tφ(T)
最后推出:
A
n
s
=
φ
(
T
)
Ans = \varphi(T)
Ans=φ(T)
此题得解。
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll euler(ll x){
ll res = x;
for(int i=2 ;i*i<=x ;i++){
if(x%i == 0){
res = res/i*(i-1);
while(x%i==0) x/=i;
}
}
if(x>1) res = res/x*(x-1);
return res;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
ll n,m;
scanf("%I64d%I64d",&n,&m);
ll ans = 0;
for(ll i=1 ;i*i<=n ;i++){
if(n%i == 0){
if(i >= m){
ans += euler(n/i);
}
if((n/i)>=m && n/i != i){
ans += euler(i);
}
}
}
printf("%I64d\n",ans);
}
return 0;
}