前言
本文给出莫比乌斯函数的模板以及数论分块的操作
莫比乌斯反演的作用是将一些不方便计算的数论函数转变为好计算的函数(特指莫比乌斯函数和欧拉函数),
反演的本质也就是狄利克雷卷积的使用,其满足交换律、结合律、分配律
且 单位元 为
ϵ
\epsilon
ϵ ,若
f
f
f、
g
g
g为积性函数,则
f
∗
g
f*g
f∗g也为积性函数
μ
∗
1
=
ϵ
\mu * 1 = \epsilon
μ∗1=ϵ
ϕ
∗
1
=
i
d
\phi * 1 = id
ϕ∗1=id
1
∗
1
=
d
1 * 1=d
1∗1=d
i
d
∗
1
=
σ
id*1=\sigma
id∗1=σ
f
∗
ϵ
=
f
f * \epsilon = f
f∗ϵ=f 莫比乌斯反演:
f
=
g
∗
1
⟹
g
=
f
∗
μ
f = g * 1 \implies g = f * \mu
f=g∗1⟹g=f∗μ一个有用的定理:
d
(
m
n
)
=
∑
i
∣
m
∑
j
∣
n
[
g
c
d
(
i
,
j
)
=
1
]
d(mn)=\sum_{i|m}\sum_{j|n}[gcd(i,j)=1]
d(mn)=i∣m∑j∣n∑[gcd(i,j)=1]
一、例题
很好的入门题 洛谷P2522
二、思路及代码
1.思路
问题可以进行区间转化,进而只需求以下和式:
∑
i
=
1
m
∑
j
=
1
n
[
g
c
d
(
i
,
j
)
=
k
]
\sum_{i=1}^{m} \sum_{j=1}^{n}[gcd(i, j)=k]
i=1∑mj=1∑n[gcd(i,j)=k]我们对其稍加变换,进而只需求:
∑
i
=
1
⌊
m
k
⌋
∑
j
=
1
⌊
n
k
⌋
[
g
c
d
(
i
,
j
)
=
1
]
\sum_{i=1}^{ \lfloor \frac{m}{k} \rfloor} \sum_{j=1}^{\lfloor \frac{n}{k} \rfloor}[gcd(i, j)=1]
i=1∑⌊km⌋j=1∑⌊kn⌋[gcd(i,j)=1]接下来开始莫比乌斯反演
∑
i
=
1
⌊
m
k
⌋
∑
j
=
1
⌊
n
k
⌋
[
g
c
d
(
i
,
j
)
=
1
]
=
∑
i
=
1
⌊
m
k
⌋
∑
j
=
1
⌊
n
k
⌋
ϵ
(
g
c
d
(
i
,
j
)
)
=
∑
i
=
1
⌊
m
k
⌋
∑
j
=
1
⌊
n
k
⌋
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
=
∑
d
≥
1
⌊
m
k
d
⌋
n
k
d
⌋
μ
(
d
)
\sum_{i=1}^{ \lfloor \frac{m}{k} \rfloor} \sum_{j=1}^{\lfloor \frac{n}{k} \rfloor}[gcd(i, j)=1] \\ =\sum_{i=1}^{ \lfloor \frac{m}{k} \rfloor} \sum_{j=1}^{\lfloor \frac{n}{k} \rfloor} \epsilon(gcd(i, j)) \\= \sum_{i=1}^{ \lfloor \frac{m}{k} \rfloor} \sum_{j=1}^{\lfloor \frac{n}{k} \rfloor} \sum_{d|gcd(i, j)} \mu(d) \\ =\sum_{d \ge1}\lfloor \frac{m}{kd} \rfloor\frac{n}{kd} \rfloor \mu(d)
i=1∑⌊km⌋j=1∑⌊kn⌋[gcd(i,j)=1]=i=1∑⌊km⌋j=1∑⌊kn⌋ϵ(gcd(i,j))=i=1∑⌊km⌋j=1∑⌊kn⌋d∣gcd(i,j)∑μ(d)=d≥1∑⌊kdm⌋kdn⌋μ(d)
至此,式子就推完了,可以利用数论分块进行求和
2.代码
代码如下:
#include <iostream>
#define int long long
using namespace std;
const int maxn = 5e4 + 4;
int miu[maxn], premiu[maxn];
int prime[maxn], pn;
bool isp[maxn];
void miu_table() {
for (int i = 1; i < maxn; i++) isp[i] = true;
miu[1] = 1, isp[1] = false;
for (int i = 2; i < maxn; i++) {
if (isp[i]) prime[pn++] = i, miu[i] = -1;
for (int j = 0; j < pn && i * prime[j] < maxn; j++) {
isp[i * prime[j]] = 0;
if (i % prime[j] == 0) {
miu[i * prime[j]] = 0;
break;
}
miu[i * prime[j]] = -miu[i];
}
}
for (int i = 1; i < maxn; i++) premiu[i] = miu[i] + premiu[i - 1];
}
int solve(int n, int m) {
int ans = 0; // 二维分块
for (int l = 1, r; l <= min(n, m); l = r + 1) {
r = min(n / (n / l), m / (m / l)); // [l, r]内(n / i) * (m / i)值一样
ans += (premiu[r] - premiu[l - 1]) * (n / l) * (m / l);
}
return ans;
}
signed main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int t;
scanf("%lld", &t);
miu_table();
while (t--) {
int a, b, c, d, k;
scanf("%lld%lld%lld%lld%lld", &a, &b, &c, &d, &k);
int ans = solve(b / k, d / k) - solve((a - 1) / k, d / k) -
solve(b / k, (c - 1) / k) + solve((a - 1) / k, (c - 1) / k);
printf("%lld\n", ans);
}
return 0;
}