曾经学过,但当时只是浅薄了理解了一下,并没有学会实际应用,所以又来学了一遍
——前言
前置定义
数论函数
积性函数
狄利克雷卷积
常见积性函数
\[\begin{aligned}
1(n)&=1\\
\epsilon(n)&=[n=1]\\
\sigma_k(n)&=\sum_{i|n}i^k\\
id_k(n)&=n^k\\
\varphi(n)&=\sum_{i=1}^n[\gcd(i,n)=1]\\
\mu(n)&=
\left\{\begin{matrix}
1&n=1\\
(-1)^x&\prod_{i=1}^x k_i=1\\
0&\max\{k_i\}\geq 2
\end{matrix}\right.
\end{aligned}\]
常见积性函数之间的变换及证明
\[\begin{aligned}
(\mu*1)(n)&=\sum_{d|n}\mu(d)1(\frac{n}{d})\\
&=\sum_{d|n}\mu(d)\\
&=[n=1]\\
&=\epsilon(n)
\end{aligned}\]
\[\begin{aligned}
(\varphi*1)(n)&=\sum_{d|n}\varphi(d)1(\frac{n}{d})\\
&=\sum_{d|n}\varphi(d)
\end{aligned}\]
考虑枚举 \(d\) 表示 \(1\sim n\) 中某个数与 \(n\) 的 \(\gcd\) 为 \(d\),那么当 \(d\in[1,n]\) 时,得到的 \(\sum_i^n[\gcd(i,n)=d]\) 的和就是 \(n\)
现在考虑如何求 \(\sum_i^n[\gcd(i,n)=d]\),把 \(n\) 拆分成 \(d\times \frac{n}{d}\) 的形式,考虑一个满足条件的 \(i(i\leq n)\),将 \(i\) 拆分成 \(k\times d\) 的形式,显然有 \(k\leq \frac{n}{d}\),又因为 \(\gcd(i,n)=d\),所以需要满足 \(k\perp \frac{n}{d}\),那么满足条件的 \(i\) 只会有 \(\varphi(\frac{n}{d})\) 个,所以容易得到
\[\begin{aligned}
\sum_{d|n}\varphi(\frac{n}{d})&=id_1(n)\\
\because \sum_{d|n}\varphi(\frac{n}{d})&=\sum_{d|n}\varphi(d)\\
&=(\varphi*1)(n)\\
\therefore(\varphi*1)(n)&=id_1(n)
\end{aligned}
\]
设 \(f(d)\) 表示 \(1\sim n\) 中与 \(n\) 的 \(\gcd\) 为 \(d\) 的个数,\(g(d)\) 表示 \(1\sim n\) 中与 \(n\) 的 \(\gcd\) 为 \(d\) 的倍数的个数
显然有
\[g(d)=[d|n]\frac{n}{d}
\]
由莫比乌斯反演得
\[\begin{aligned}
f(g)&=\sum_{g|d}\mu(\frac{d}{g})g(d)\\
&=\sum_{g|d,d|n}\mu(\frac{d}{g})\frac{n}{d}\\
f(1)&=\sum_{d|n}\mu(d)\frac{n}{d}
\end{aligned}
\]
容易发现 \(f(1)\) 其实就是 \(1\sim n\) 中与 \(n\) 互质的个数
\[\begin{aligned}
(\mu*id_1)(n)&=\sum_{d|n}\mu(d)1(\frac{n}{d})\\
&=f(1)\\
&=\varphi(n)
\end{aligned}
\]
杜教筛
杜教筛常用于求解一些数论函数的前缀和,由杜瑜皓杜老师研究并发明
经常与洲阁筛、\(min25\) 筛相提并论,因其对选手的数学能力要求不高、较为优秀的时间复杂度和代码实现的简短,常作为三大筛法的优先学习对象
下面这个图展示了三个筛法在不同数据范围下的算法效率,容易发现杜教筛的复杂度变化相对于其他两个来说是较为平稳的
基本问题
给定一个数论函数 \(f(x)\),求出 \(f(x)\) 的第 \(n\) 项前缀和 \(S(n)\),\(n\leq 10^{10}\)
算法核心
杜教筛的核心是构造一个数论函数 \(g(x)\),从而得到 \(h=f*g\),\(h\) 的第 \(n\) 项前缀和很好求出,然后通过一些推导从而求出 \(S(n)\)
通项公式
因为 \(f*g\) 的前缀和很好求,考虑用它去推得 \(S(n)\)
\[\sum_{i=1}^n(f*g)(i)=\sum_{i=1}^n\sum_{d|i}f(d)g(\frac{i}{d})
\]
然后我们将后面那个狄利克雷卷积的形式改变一下
我们原来的表示是
\[a[i]=\sum_{d|i}b[d]c[\frac{i}{d}]
\]
我们现在改成
\[a[i]=\sum_{i=j\times k}b[j]c[k]
\]
显然是正确的吧
然后又因为我们只需要求卷积后每项的和,所以只需要考虑每对 \(j,k\) 对和的贡献就行了,所以上面推到一半的式子可以转化成
\[\begin{aligned}
\because \sum_{d=1}^ng(d)\sum_{i=1}^{\left \lfloor\frac{n}{d}\right \rfloor} f(i)&=\sum_{d=1}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)\\
\therefore \sum_{i=1}^n(f*g)(i)&=\sum_{d=1}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)\\
g(1)S(n)&=\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)\\
S(n)&=\frac{\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
\end{aligned}
\]
最后我们得到了一个 \(S(n)\) 的递推式,整除分块递归求解即可
算法效率
通常情况下,\(\sum_{i=1}^n(f*g)(i)\) 是可以 \(\mathcal{O}(1)\) 求出的,一段 \([l,r]\) 的 \(g(d)\) 之和也可以 \(\mathcal{O}(1)\) 得到,所以时间复杂度主要集中在递归求解上,而且通常情况下会对 \(S(n)\) 进行记忆化
由于整除分块的复杂度是 \(\mathcal{O}(\sqrt{n})\),而且 \(\left \lfloor\frac{n}{d}\right \rfloor\) 的取值只有 \(\sqrt{n}\) 个,所以最后的复杂度是
\[\sum_{i=1}^{\sqrt{n}}(i+\sqrt{\frac{n}{i}})=n^{\frac{3}{4}}
\]
我们可以通过线性筛先预处理出来较小的 \(n\) 的 \(S(n)\), \(n\) 很小时就没必要再递归下去了,这样复杂度可以优化到 \(\mathcal{O}(n^{\frac{2}{3}})\)
算法模板
提前会有一个线性筛,剩下的就只剩一个递归函数了
inline ll F (register ll n) {
if (n <= 3e6) return sumf[n]; // 预处理出 n 较小时的前缀和
if (f[n]) return f[n]; // 记忆化,如果求过这个值,就不需要再递归一遍了
register ll ans = sum (f * g); // 这是 f * g 的 n 项前缀和
for (register ll l = 2, r; l <= n; l = r + 1) // 整除分块
r = n / (n / l), ans -= (sumg[r] - sumg[l - 1]) * F (n / l);
// [l,r] 的 F (n / l) 是一样的,对 g(x) 求个和即可
return f[n] = ans / g[1]; // 别忘了除上 g(1)
}
常见数论函数的前缀和推导
- \(f(n)=\mu(n),S(n)=\sum_{i=1}^{n}\mu(i)\)
构造函数 \(g(n)=1(n)\),容易得到 \(f*g=\epsilon\)(详见上面证明),套用公式
\[\begin{aligned}
S(n)&=\frac{\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\sum_{i=1}^n\epsilon(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{1}\\
&=1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)
\end{aligned}
\]
因为 \(g(n)=1(n)\),所以 \(g(n)\) 的前缀和可以直接 \(\mathcal{O}(1)\) 求,就可以直接套用模板做了
- \(f(n)=\varphi(n),S(n)=\sum_{i=1}^{n}\varphi(i)\)
构造函数 \(g(n)=1(n)\),容易得到 \(f*g=id_1\)(详见上面证明),套用公式
\[\begin{aligned}
S(n)&=\frac{\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\sum_{i=1}^nid_1(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\frac{n(n+1)}{2}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\frac{n(n+1)}{2}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{1}\\
&=\frac{n(n+1)}{2}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)
\end{aligned}
\]
同样可以套用模板去做
- \(f(n)=\mu(n)n,S(n)=\sum_{i=1}^{n}\mu(i)i\)
构造函数 \(g(n)=id_1(n)\)
\[\begin{aligned}
(f*g)(i)&=\sum_{d|i}f(d)g(\frac{i}{d})\\
&=\sum_{d|i}\mu(d)d\times \frac{i}{d}\\
&=\sum_{d|i}\mu(d)i\\
&=i\sum_{d|i}\mu(d)\\
&=i[i=1]\\
&=\epsilon(i)
\end{aligned}
\]
接着套用公式
\[\begin{aligned}
S(n)&=\frac{\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\sum_{i=1}^n\epsilon(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{1}\\
&=1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)
\end{aligned}
\]
因为 \(g(n)=id_1(n)\),所以可以 \(\mathcal{O}(1)\) 计算前缀和,同样可以套用模板去做
- \(f(n)=\varphi(n)n,S(n)=\sum_{i=1}^{n}\varphi(i)i\)
构造函数 \(g(n)=id_1(n)\)
\[\begin{aligned}
(f*g)(i)&=\sum_{d|i}f(d)g(\frac{i}{d})\\
&=\sum_{d|i}\varphi(d)d\times \frac{i}{d}\\
&=\sum_{d|i}\varphi(d)i\\
&=i\sum_{d|i}\varphi(d)\\
&=i\times i\\
&=i^2
\end{aligned}
\]
接着套用公式
\[\begin{aligned}
S(n)&=\frac{\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\sum_{i=1}^ni^2-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\frac{n(n+1)(2n+1)}{6}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\frac{n(n+1)(2n+1)}{6}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{1}\\
&=\frac{n(n+1)(2n+1)}{6}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)
\end{aligned}
\]
然后套用模板去做就行了
- \(f(n)=\mu(n)n^2,S(n)=\sum_{i=1}^{n}\mu(i)i^2\)
构造函数 \(g(n)=id_2(n)\)
\[\begin{aligned}
(f*g)(i)&=\sum_{d|i}f(d)g(\frac{i}{d})\\
&=\sum_{d|i}\mu(d)d^2\times \frac{i^2}{d^2}\\
&=\sum_{d|i}\mu(d)i^2\\
&=i^2\sum_{d|i}\mu(d)\\
&=i^2[i=1]\\
&=\epsilon(i)
\end{aligned}
\]
接着套用公式
\[\begin{aligned}
S(n)&=\frac{\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\sum_{i=1}^n\epsilon(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{1}\\
&=1-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)
\end{aligned}
\]
因为 \(g(n)=id_2(n)\),所以前缀和直接就是 \(\frac{n(n+1)(2n+1)}{6}\),同样可以套用模板去做
- \(f(n)=\varphi(n)n^2,S(n)=\sum_{i=1}^{n}\varphi(i)i^2\)
构造函数 \(g(n)=id_2(n)\)
\[\begin{aligned}
(f*g)(i)&=\sum_{d|i}f(d)g(\frac{i}{d})\\
&=\sum_{d|i}\varphi(d)d^2\times \frac{i^2}{d^2}\\
&=\sum_{d|i}\varphi(d)i^2\\
&=i^2\sum_{d|i}\varphi(d)\\
&=i^2\times i\\
&=i^3
\end{aligned}
\]
接着套用公式
\[\begin{aligned}
S(n)&=\frac{\sum_{i=1}^n(f*g)(i)-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\sum_{i=1}^ni^3-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\frac{n^2(n+1)^2}{4}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{g(1)}\\
&=\frac{\frac{n^2(n+1)^2}{4}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)}{1}\\
&=\frac{n^2(n+1)^2}{4}-\sum_{d=2}^ng(d)S(\left \lfloor\frac{n}{d}\right \rfloor)
\end{aligned}
\]
\(\sum g(d)\)的求法跟上面一样,接着还是套用模板去做就行了
例题
模板题,其实就是前两个推导,就不写题解了
选数,题解
神犇和蒟蒻,题解
如果你懂了杜教筛核心思想,做题基本上就差不多了
本质上是这个递推函数的变形,剩下的问题主要就是 \(g(n)\) 的构造和 \(f*g\) 的推导,数学足够强的话应该也不构成问题
因为博主太菜了,或许不会再更新其他两个筛法的博客了
例题可能会看心情实时更新,但博主是个老鸽子了,毕竟连游记都懒得写
——后记