杜教筛

目的:对于一个积性函数 f\operatorname{f},我们要对它求前缀和,但是数据范围较大(101010^{10}),不支持 Θ(n)\varTheta(n),这时,我们就可以使用杜教筛。

S(n)=i=1nf(i)g(1)S(n)=i=1n(fg)(i)i=2ng(i)S(ni)\operatorname{S}(n)=\sum_{i=1}{n}\operatorname{f}(i) \\ \operatorname{g}(1)\operatorname{S}(n)=\sum_{i=1}^n(\operatorname{f}*\operatorname{g})(i)-\sum_{i=2}^n\operatorname{g}(i)\operatorname{S}(\lfloor\frac{n}{i}\rfloor)

其中,g\operatorname{g} 是我们自己设的函数,经常为 id,1,ε\operatorname{id},\operatorname{1},\varepsilon 等,它们的 g(1)=1\operatorname{g}(1)=1,所以常可忽略。

给一个求 φ\varphi 的板子。

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
using ll = long long;
constexpr int N = 1e6;
ll sphi[N + 10];
unordered_mapSphi;
inline ll Sumphi(int n) {
if (n <= N) return sphi[n];
if (Sphi[n]) return Sphi[n];
ll ret = 1ll * n * (n + 1) / 2;
for (int l = 2, r; l <= n; l = r + 1)
r = n / (n / l),
ret -= (r - l + 1) * Sumphi(n / l);
return Sphi[n] = ret;
}
vectorprm;
bool vis[N + 10];
ll phi[N + 10];
void iniit() {
sphi[1] = phi[1] = 1;
for (int i = 2; i <= N; i++) {
if (!vis[i]) {
prm.push_back(i);
phi[i] = i - 1;
}
for (int& v : prm) {
if (i * v > N) break;
vis[i * v] = true;
if (i % v == 0) {
phi[i * v] = phi[i] * v;
break;
}
phi[i * v] = phi[i] * phi[v];
}
sphi[i] = sphi[i - 1] + phi[i];
}
}

大致板子长这样。

1
2
3
4
5
6
7
ll GetSum(int n) {
ll ans = f_g_sum(n);
for (ll l = 2, r; l <= n; l = r + 1)
r = (n / (n / l)),
ans -= (g_sum(r) - g_sum(l - 1)) * GetSum(n / l);
return ans;
}

杜教筛
https://www.lzj-blog.top/2025/03/05/杜教筛/
作者
lzj
发布于
2025年3月5日
许可协议