数论:筛子合集

这里主要介绍三种筛子:杜教筛,PN 筛,min_25 筛。
它们可以针对不同特点的数论函数在亚线性的复杂度内求出它的前缀和,即解决如下问题:
给定数论函数 \(f(x)\),求 \(S(n)=\sum_{i=1}^n f(i)\),其中 \(n\) 的规模大致在 \(10^9\)\(10^{10}\)

由于很多地方的计算(比如复杂度)最后一步要用到积分,但是我不会,所以会省略最后积分的步骤。

若无特殊说明 \(p\) 都是质数。


杜教筛

使用条件

可以构造一个数论函数 \(g\),满足:

  1. \(g\) 可以快速求前缀和。
  2. \(f*g\) 可以快速求前缀和。

注意: 不一定只有积性函数可以用杜教筛求解。

思想

\[\begin{aligned} \sum_{i=1}^n f*g(i) &=\sum_{i=1}^n \sum_{d|i} g(d)f(\frac{i}{d}) \\ &=\sum_{d=1}^n \sum_{x=1}^{\lfloor \frac{n}{d} \rfloor} g(d)f(x) \\ &=\sum_{d=1}^n g(d)S(\lfloor \frac{n}{d} \rfloor) \end{aligned} \]

移项,得到:

\[g(1)S(n)=\sum_{i=1}^n f*g(i) - \sum_{d=2}^n g(d)S(\lfloor \frac{n}{d} \rfloor) \]

这就是杜教筛的核心式子。
注意到我们只需要能够快速求出 \(f*g\) 的前缀和和 \(g\) 的前缀和,就可以用数论分块递归地求出 \(S(n)\) 的值了。

下面是核心代码:

int S(int n){  //求 S(n) 
	int l,r,ans=s_fg(n);  //s_fg(n) 是 f*g 的前缀和  
	for(l=2;l<=n;l=r+1){  //数论分块 
		r=n/(n/l);
		ans-=(s_g(r)-s_g(l-1))*S(n/l); //s_g(n) 是 g 的前缀和
	}
	return ans/g(1);  
}

时间复杂度

我们证明,在使用记忆化搜索之后,上述代码的复杂度是 \(O(n^{\frac{3}{4}})\)

引理:\(R(n)=\{ \lfloor \frac{n}{d} \rfloor | 2\le d\le n\}\),则任意的 \(x\in R(n)\) 都满足 \(R(x)\subseteq R(n)\)

证明:设 \(x=\lfloor \frac{n}{i} \rfloor\),则 \(y\in R(x)\) 满足 \(y=\lfloor \frac{x}{j} \rfloor=\lfloor \dfrac{\lfloor \frac{n}{i} \rfloor}{j} \rfloor\)=\lfloor \frac{n}{ij} \rfloor \in R(n)$。

这意味着,使用记忆化搜索以后,要求的不同的 \(S(x)\) 只有 \(|R(n)|=O(\sqrt{n})\) 种。
省去递归调用的时间,因为求每个 \(S(x)\) 时数论分块需要 \(O(\sqrt{x})\) 地遍历,所以求 \(S(n)\) 的总复杂度 \(T(n)\) 是:

\[\begin{aligned} T(n) &=O(\sqrt{n})+\sum_{x\in R(n)} O(\sqrt{x}) \\ &=O(\sqrt{n})+\sum_{i=1}^{\sqrt{n}} O(\sqrt{i}) + \sum_{i=2}^{\sqrt n} O(\sqrt{\frac{n}{i}}) \\ &(省去积分的过程)\\ &=O(n^{\frac{3}{4}}) \end{aligned} \]

进一步的,我们可以提前预处理 \(x\le m\)\(m\ge \lfloor \sqrt n \rfloor\)) 时 \(S(x)\) 的答案(当然如果 \(f\) 的性质不是很好可能没法预处理),那么复杂度变成:

\[\begin{aligned} T(n) &=O(\sqrt{n})+\sum_{x\in R(n),x>m} O(\sqrt{x}) \\ &=\sum_{i=1}^{\lfloor \frac{n}{m} \rfloor} O(\sqrt{\frac{n}{i}}) \\ &(省去积分的过程)\\ &=O(\frac{n}{\sqrt m}) \end{aligned} \]

如果预处理的复杂度是 \(O(m)\)(比如 \(f\) 是积性函数时可以线性筛),那么平衡得最优复杂度是 \(O(n^{\tfrac{2}{3}})\)

实现细节

如果使用 unordered_map 或者哈希表存储结果来记忆化,常数会有点大,我们考虑不用哈希表。具体地,我们开一个数组 dp 记录答案,并取一个阈值 \(B=\lfloor \sqrt{n} \rfloor\)

  1. 如果 \(x\le B\),就把 \(S(x)\) 存到 dp[x] 中。
  2. 否则,就把 \(S(x)\) 存到 dp[B+n/x] 中。

注意: 如果使用的是 \(O(n^{\frac{2}{3}})\) 的写法,\(x\le B\) 的答案已经预处理好了,可以不用管。

接下来我们证明不会出现两个 \(x,y\in R(n)\) 他们用的是 dp 的同一个位置。

证明:\(\le B\) 的情况显然不会出问题,我们只需要证明 \(x,y>B\) 的情况即可。
假设 \(x\ne y\) 但是 \(\lfloor \frac{n}{x} \rfloor=\lfloor \frac{n}{y} \rfloor\),我们设 \(x=\lfloor \frac{n}{i} \rfloor,y=\lfloor \frac{n}{j} \rfloor\)
那么有 \(\lfloor \tfrac{n}{\lfloor \tfrac{n}{i} \rfloor} \rfloor=\lfloor \tfrac{n}{\lfloor \tfrac{n}{j} \rfloor} \rfloor\),这意味着 \(i,j\) 属于同一个块(因为他们所属块的右端点一样),从而必有 \(\lfloor \frac{n}{i} \rfloor = \lfloor \frac{n}{j} \rfloor\),这与 \(x\ne y\) 矛盾了,所以不会出现这种情况。

所以这样存储是正确的。

应用

(1). 求 \(\mu\) 的前缀和。
\(f=\mu,g=1\),则 \(f*g=\mu*1=\epsilon\),显然可以快速求出 \(1,\epsilon\) 的前缀和。

(2). 求 \(\varphi\) 的前缀和。
\(f=\varphi,g=1\),则 \(f*g=\varphi*1=id\),显然可以快速求出 \(1,id\) 的前缀和。

(3). 求 \(\mu \cdot id_k,\varphi \cdot id_k\)
处理这类带点乘的式子有一个技巧,就是根据 \((A\cdot C)*(B\cdot C)=(A*B)\cdot C\)(证明直接展开即可),比如对于上面这两个东西可以这样处理,取 \(g=id_k\)

  • \((\mu \cdot id_k) * id_k = (\mu \cdot id_k) * (1 \cdot id_k) = (\mu*i)\cdot id_k = \epsilon \cdot k =\epsilon\)
  • \((\varphi \cdot id_k) * id_k = (\varphi \cdot id_k) * (1 \cdot id_k) = (\mu*i)\cdot id_k = id \cdot id_k = id_{k+1}\)

给两道典题:


PN 筛

使用条件

  1. \(f\) 是积性函数。
  2. 存在一个函数 \(g\) 使得:
  • \(g\) 是积性函数。
  • \(g\) 在质数 \(p\) 上的取值和 \(f\) 相等,即 \(f(p)=g(p)\)
  • \(g\) 易求前缀和,下面设 \(G(n)\) 表示 \(g\) 的前缀和。

思想

Powerful Number(PN)

先介绍什么是 PN。

定义:\(n=\prod p_i^{c_i}\) 满足 \(\forall i,c_i\ge 2\),则 \(n\) 是一个 Powerful Number,简称 PN。
性质一: 任何一个 PN 都可以表示成 \(a^2b^3\) 的形式。

证明:依次考虑每个质因子,若指数是偶数就放到 \(a\),否则先把 \(3\) 个放到 \(b\) 其余的放到 \(a\)

性质二: \(n\) 以内的 PN 只有 \(\sqrt{n}\) 个。

证明:考虑枚举 \(a\) 并计算满足条件的 \(b\) 的个数,之后积分可证。

性质二我们要找出所有 PN 的话只需要 dfs 枚举每个指数的因数就可以了。

PN 筛

他的思想和杜教筛有点像。
由于 \(f,g\) 是积性函数,所以必定存在一个数论函数 \(h\) 满足 \(f=g*h\)(或者说 \(h=f/g\),逆元是在狄利克雷卷积意义下的),且 \(h\) 也是积性函数。
那么 \(f(p)=g(1)h(p)+g(p)h(1)=h(p)+g(p)\),而因为 \(f(p)=g(p)\),所以 \(h(p)=0\),而又因为 \(h\) 是积性函数,所以 \(h\) 在所有非 PN 数处的取值都是 \(0\)

我们有:

\[\begin{aligned} S(n) &= \sum_{i=1}^n f(i) \\ &= \sum_{i=1}^n \sum_{d|i} g(\frac{i}{d})h(d) \\ &= \sum_{d=1}^n h(d)G(\frac{n}{d}) \end{aligned} \]

因为上面我们证明了 \(h\) 仅在 PN 处有值,所以上面有用的 \(d\) 只有 \(O(\sqrt{n})\) 个,也就是说我们只需要在每个 PN 处计算 \(h(d)G(\frac{n}{d})\) 并累加到答案里即可,而因为 \(h\) 是积性函数,所以我们如果知道每个 \(h(p^k)\) 的值,就可以在 dfs 搜索 PN 的过程中计算出 \(h(d)\)

\(h(p^k)\) 一般是两种求法,一种是根据定义式 \(h=f/g\) 推导出 \(h\) 的通项公式,这种比较吃操作而且因题目而异,主要介绍令一种不太吃手法的方法:
因为 \(f(p^k)=\sum_\limits{c=0}^k g(p^c)h(p^{k-c})\),所以 \(h(p^k)=f(p^k)-\sum_\limits{c=1}^{k} g(p^c)h(p^{k-c})\),可以直接枚举 \(c\) 进行计算。

时间复杂度

先看预处理 \(h(p^k)\) 的复杂度(如果你用第一种做法那显然不需要预处理)。
因为 \(p\) 只需要处理到 \(\sqrt{n}\)(更大的质数不可能作为 PN 的质因数),而 \(\sqrt{n}\) 以内的质数约有 \(O(\frac{\sqrt{n}}{\log n})\) 个,每个质数的指数不超过 \(\log n\),算的时候要 \(O(k)\) 枚举,所以这部分的复杂度是 \(O(\sqrt{n} \log n)\),但是肯定跑不满。

再看 dfs 搜索 PN 累加答案的复杂度,由于 PN 的个数是 \(O(\sqrt{n})\) 个,所以如果 \(G\) 可以 \(O(1)\) 求那么复杂度就是 \(O(\sqrt{n})\)

实现细节

搜索 PN 的 dfs 不要写假了。

例题

【模板】Min_25 筛

因为 \(f(p)=p(p-1)\),所以我们可以取 \(g(n)=n\varphi(n)\),显然他是积性函数。
我们可以用杜教筛求 \(g\) 的前缀和,然后上 PN 筛即可。
此时复杂度瓶颈在于杜教筛,总复杂度为杜教筛的 \(O(n^{\tfrac{2}{3}})\)

Tip:这题实际上可以得到 \(h(p^k)=p^k(p-1)(k-1)\)


OK 简单的讲完了,现在要开始讲最麻烦的 min_25 了。

min_25 筛

使用条件

  1. \(f\) 是积性函数。
  2. \(f(p)\) 是关于 \(p\) 的简单多项式,一般次数不超过 \(10\)
  3. \(f(p^k)\) 可以快速求值。

思想

min_25 筛分为两个部分。

质数的 \(c\) 次幂前缀和

我们先解决一个问题,给定 \(n\),如何求 \(n\) 以内的所有质数的 \(c\) 次幂的和。

我们约定:

  • \(mn(x)\) 表示 \(x\) 的最小质因子,特殊的,\(mn(1)=+\infty\)(注意 \(mn(1)\) 不是 \(0\)!)。
  • \(p_i\) 表示第 \(i\) 小的质数。
  • \(p_m\) 是最大的 \(\le \sqrt{n}\) 的质数。
  • \(P_k\) 表示前 \(k\) 个质数构成的集合。

模仿线性筛的思路,在一个合数的最小质因子处把他筛去,由于一个合数必然有一个 \(\le \sqrt{n}\) 的质因子,所以我们只需要把质数处理到 \(\sqrt{n}\) 就可以筛完所有的合数。
\(S(n,k)=\left\{ x | 1\le x \le n,mn(x)>p_k \right\}\) 表示处理完了前 \(k\) 个质数,前 \(n\) 个数中没被筛去的数的集合(根据约定 \(1\) 一定属于 \(S(n,k)\)),并设 \(h(n,k)=\sum_{x\in S(n,k)} x^c\),则 \(h(n,m)-1+\sum_\limits{i=1}^m p_i^c\) 就是答案。

\(D(n,k)=\left\{ x | 1\le x \le n,mn(x)=p_k \right\}\) 表示第 \(k\) 轮删去的数的集合,那么根据定义有 \(S(n,k)=S(n,k-1)-D(n,k)\)
一个显然的事情是如果一个数在第 \(k\) 轮被删除,设它是 \(p_k\cdot x\),那么 \(x\le \lfloor \frac{n}{p_k} \rfloor\),并且 \(x\) 在前 \(k-1\) 轮没被删掉,即 \(x\in S(\lfloor \frac{n}{p_k} \rfloor,k-1)\)。同时,如果一个数 \(x\in S(\lfloor \frac{n}{p_k} \rfloor,k-1)\),那么 \(p_k\cdot x\) 一定会在第 \(k\) 轮被删掉。所以这就构成了一个双射,于是我们得到了:\(D(n,k)=p_kS(\lfloor \frac{n}{p_k} \rfloor,k-1)\)
再带回到上面那个式子就得到了 \(S\) 的递推式:\(S(n,k)=S(n,k-1)-p_kS(\lfloor \frac{n}{p_k} \rfloor,k-1)\),进一步可以推出 \(h\) 的递推式:\(h(n,k)=h(n,k-1)-p_k^c h(\lfloor \frac{n}{p_k} \rfloor,k-1)\)
边界是 \(h(n,0)=\sum_{i=1}^n i^c\),可以直接拉插求出自然数幂和。

如果直接用这个递推式去做,那么第一维是 \(O(\sqrt{n})\) 的,第二维是 \(O(\frac{\sqrt{n}}{\log n})\) 的,于是我们得到了一个 \(O(\frac{n}{\log n})\) 的完全过不去的做法。

注意到我们现在的答案是 \(h(n,m)-1+\sum_\limits{i=1}^m p_i^c\),不太美观,考虑设 \(S'(n,k)=S(n,k)\cup P_k\)\(h'(n,k)=\sum_{x\in S'(n,k)} x^c\),那么答案就变成了 \(h'(n,m)-1\)
并且 \(S'\) 有一个很好的性质:根据我们一开始的分析,当 \(p_k>\sqrt{n}\) 时,\(p_k\) 无法筛掉除了自己之外的任何数,所以 \(S'(n,k)=S'(n,k-1)\)
仍然和上面一样我们去求出 \(S'\) 的递推式:

\[\begin{aligned} S'(n,k) &=S(n,k)+P_k \\ &=S(n,k-1)-p_kS(\lfloor \frac{n}{p_k} \rfloor,k-1)+P_k \\ &=S'(n,k-1)-p_kS(\lfloor \frac{n}{p_k} \rfloor,k-1)+p_k \\ &=S'(n,k-1)-p_k\left[S'(\lfloor \frac{n}{p_k} \rfloor,k-1)-P_{k-1}\right] +p_k \\ &=S'(n,k-1)-p_k\left[S'(\lfloor \frac{n}{p_k} \rfloor,k-1)-(S'(p_{k-1},k-1)-1)\right] +p_k \\ &=S'(n,k-1)-p_k\left[S'(\lfloor \frac{n}{p_k} \rfloor,k-1)-S'(p_{k-1},k-1)\right] \\ \end{aligned} \]

同理,\(h'\) 的递推式为:

\[h'(n,k)=h'(n,k-1)-p_k^c(h'(\lfloor \frac{n}{p_k} \rfloor,k-1)-h'(p_{k-1},k-1))。 \]

虽然看着和 \(h\) 好像是一样的,但是因为当 \(p_k>\sqrt{n}\) 时,\(S'(n,k)=S'(n,k-1)\),所以对于每个 \(x\)\(h'(x,k)\) 第二维的 \(k\)\(O(\frac{\sqrt{x}}{\log x})\)(原先不管 \(x\) 是什么第二维始终是 \(O(\frac{\sqrt{n}}{\log n})\))的,那么复杂度就变成了 \(O(\tfrac{n^{\frac{3}{4}}}{\log n})\)

会发现我们实际上得到了所有 \(\lfloor \frac{n}{i} \rfloor\) 的答案。

积性函数前缀和

现在我们要求一个满足开头的使用条件的积性函数 \(f\) 的前缀和。
借鉴第一部分的思想,设 \(g(n,k)=\sum_\limits{1\le x\le n,mn(x)\ge p_k} f(x)\),转移考虑枚举 \(x\) 的最小质因子 \(p_t\) 并枚举他的指数 \(c\),则:

\[g(n,k)=\sum_{t\ge k} \sum_{c\ge 1,p_t^c\le n} f(p_t^c)g(\lfloor \tfrac{n}{p_t^c} \rfloor,t+1) \]

显然当 \(p_t> \sqrt{n}\) 时,符合条件的 \(x\) 只有 \(p_t\) 一个,所以我们枚举 \(p_t\) 只需要枚举到 \(\sqrt{n}\),对于 \(>\sqrt{n}\) 的质数用我们第一部分预处理出来的东西算即可。

复杂度可以视为 \(O(\tfrac{n^{\frac{3}{4}}}{\log n})\),证明我不会,可以看原论文。

实现细节

  1. 预处理 \(h'\) 的时候因为我们只需要用最后的东西,所以可以把 \(k\) 这一维滚掉,减少时空常数。
  2. min_25 筛的搜索过程可以不用记忆化。

例题

I. 【模板】Min_25 筛

板子,不多讲了

II. 简单的函数

\(p=2\)\(f(p)=p+1\),其他情况都有 \(f(p)=p-1\),所以特殊处理一下 \(2\) 的情况直接上 min_25 即可。

III. 【UR #13】Sanrd

严格来讲这题只是用到了 min_25 的思想。

\(f(x)\) 表示 \(x\) 的非严格次大质因子(\(f(1)=0\)),相当于求这个东西的前缀和。
仍然考虑设 \(S(n,k)\) 表示 \(\le n\) 且最小质因子 \(\ge p_k\) 的数构成的集合,思考在上面的递推式中如果把 \(x\in S(\lfloor \tfrac{n}{p_t^c} \rfloor,t+1)\) 乘上 \(p_t^c\) 会发生什么:

  1. \(x\) 是合数:\(f(x\times p_t^c)=f(x)\)
  2. \(x\) 是质数:那么 \(f(x\times p_t^c)=p_t\)
  3. \(x=1\)\(f(x\times p_t^c)=[c\ge 2]\cdot p_t\)

根据这个直接做即可。
由于质数在这题中不产生贡献,所以我们都不需要做第一部分。


参考资料&致谢名单

posted @ 2025-07-22 18:15  Green&White  阅读(16)  评论(0)    收藏  举报