杜教筛

曾经学过,但当时只是浅薄了理解了一下,并没有学会实际应用,所以又来学了一遍

——前言

前置定义

数论函数

积性函数

狄利克雷卷积

常见积性函数

\[\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^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}\]

常见积性函数之间的变换及证明

  • \(\mu*1=\epsilon\)

\[\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}\]


  • \(\varphi*1=id_1\)

\[\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} \]


  • \(\mu*id_1=\varphi\)

\(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\) 的推导,数学足够强的话应该也不构成问题

因为博主太菜了,或许不会再更新其他两个筛法的博客了

例题可能会看心情实时更新,但博主是个老鸽子了,毕竟连游记都懒得写

——后记

posted @ 2021-06-24 08:04  Rubyonlу  阅读(932)  评论(5编辑  收藏  举报