浅谈筛法
浅谈筛法
Euler 筛
P3601 签到题
P4626 一道水题 II
Eratosthenes 筛
可以证明(不是“不难证明”),Eratosthenes 筛的复杂度为 \(\Theta(n\log \log n)\)。
Dirichlet 前缀和
以 P5495 【模板】Dirichlet 前缀和 为例。
给定 \(a_1,a_2,\cdots,a_n\),求 \(b_1,b_2,\cdots,b_n\),满足
\(n\leq 2\times 10^7\)。
直接考虑 Dirichlet 前缀和。板子如下:
for (int i=1; i<=tot; ++i)
for (int j=1; j*prime[i]<=n; ++j)
a[j*prime[i]]+=a[j];
实际上是关于素数的一个高维前缀和。利用解析数论的结论不难证明时间复杂度为 \(\Theta(n\log \log n)\)。
同理我们有 Dirichlet 后缀和。
for (int i=1; i<=tot; ++i)
for (int j=n/prime[i]; j; --j)
a[j]+=a[j*prime[i]];
例题 1
给定数组 \(a_1,a_2,\cdots,a_n\)。求出 \(b_i=\sum_{k=1}^n [i\mid a_k]\)。\(n\leq 2\times 10^7\)。
Divan and Kostomuksha (hard version)
给定数列 \(a\)。可以任意重排数列,最大化
的值。\(n\leq 2\times 10^5\),\(1\leq a_i\leq 2\times 10^7\)。
我们有一些性质。
- 值相同的数一定放在一起。
证明显然。 - 最优方案中,数列一定不增。
证明显然。
设 \(cnt_i\) 为 \(a_i\) 中 \(i\) 的倍数个数,设 \(f[i]\) 为重排后的数组最后一个数含有因数 \(i\) 的最大权值。我们有
我们将 \(i\) 的倍数紧接在 \(i\cdot p_j\) 的倍数后面。接在后面的数有 \(i\cdot (cnt_i-cnt_{i\cdot p_j})\) 个,每个数贡献了 \(i\)。
注意到这是经典的 Dirichlet 后缀和的形式,时间复杂度 \(\Theta(V\log \log V)\)。
Wheel of factorization
咕咕咕。
杜教筛
杜教筛常用于求一类数论函数 \(g\) 的前缀和。要求:
- 可以构造 \(h\),使得 \(f=g\ast h\);
- \(f\) 的前缀和及 \(h\) 的单点易求。
需要注意的是,\(g\) 不必须是数论函数。
设 \(s(n)=\sum_{i=1}^n g(i)\),我们将 \(f\) 的前缀和拆开。
于是我们得到杜教筛的公式。
\(s(\left\lfloor\frac{n}{i}\right\rfloor)\) 是相同的子问题,可以递归求解。
朴素地做可以证明是 \(\Theta(n^{\frac{3}{4}})\) 的。如果预处理出 \([1,n^{\frac{2}{3}}]\) 内的 \(g\) 的前缀和则可以做到 \(\Theta(n^{\frac{2}{3}})\)。
洲阁筛
P5493 质数前缀统计
记 \(\mathbb{P}\) 为素数集,\(p_i\) 为第 \(i\) 小的素数,\(S(n)=\displaystyle\sum_{p\leq n, p\in \mathbb{P}} p^k\)。求
\(n\leq 4\times 10^{10}\),对素数 \(p\) 取模。
观察到前面是经典的数论分块形式。于是问题的关键化为了求 \(S(n)\)。
设 \(g(n,i)\) 为用前 \(i\) 个素数筛过了之后的 \(S(n)\)。初始时有 \(g(n,0)=\sum_{i=1}^n i^k\),不难利用 Lagrange 插值 \(\Theta(k)\) 求出(如果读者不会,可以阅读我写的《多项式基础》博客)。
考虑递推,我们先给出结论。
引理
合数 \(n\) 必有不大于 \(\sqrt{n}\) 的素因子。证明显然。
考虑 \(i-1\to i\) 的过程我们要筛掉哪些合数。显然是那些最小素因子恰好为 \(p_i\) 的合数。我们不妨把这些合数除以 \(p_i\)。设合数 \(n\) 满足最小素因子恰好为 \(p_i\),那么它一定没有在前 \((i-1)\) 轮中被筛掉,否则就矛盾了。于是就是 \(-p_i^k\cdot g\left(\left\lfloor\dfrac{n}{p_i}\right\rfloor,i-1\right)\) 项。
注意到前面会把小于 \(p_i\) 的素数计入(注意,素数不会被筛掉)。所以后面要减掉。
\(g(p_i-1,i-1)\) 实际上就是前 \((i-1)\) 个素数的 \(k\) 次方和,不难预处理。
注意到我们的 \(i\) 只和 \((i-1)\) 有关。于是我们可以滚掉第二维。
引理 1
\(\left\lfloor\dfrac{\left\lfloor\dfrac{n}{a}\right\rfloor}{b}\right\rfloor=\left\lfloor\dfrac{n}{ab}\right\rfloor\)。
引理 2
\(\left\lfloor\dfrac{n}{i}\right\rfloor\) 的不同值数量级为 \(\Theta(\sqrt n)\)。
所以我们实际上只会用 \(\Theta(\sqrt n)\) 数量级的第一维,每一个都是 \(\left\lfloor\frac{n}{i}\right\rfloor\) 的形式。我们不妨令 g[i]
为 \(g(\left\lfloor\frac{n}{i}\right\rfloor)\) 的值。
可以用 (unordered_)map
来处理映射关系,但是这样会使常数变大/多加一个 \(\log\)。可以开两个数组:\(id_1[x]\) 表示 \(x\)(\(x\leq \sqrt n\))的下标,\(id_2[x]\) 表示 \(\left\lfloor\dfrac{n}{x}\right\rfloor\)(\(x\geq \sqrt n\))的下标。精细实现可以减小常数。
但是你会发现一个很严重的问题:如果直接这么做是 \(\displaystyle \Theta(\frac{n}{\log n})\) 的(处理 \(\sqrt n\) 种取值,每种取值有 \(\dfrac{\sqrt n}{\log n}\) 个素因子)。
我们注意到,当 \(p_{i+1}^2\gt n\) 的时候,\(p_{i+1}^k\cdot \left(g(\left\lfloor\dfrac{n}{p_i}\right\rfloor,i-1)-g(p_i-1,i-1)\right)=p_{i+1}^k\)。
证明 显然此时 \(p_{i+1}^k\cdot g(\left\lfloor\dfrac{n}{p_i}\right\rfloor,i-1)\) 中只有 \([1,n]\) 中素数的贡献,因为里面的合数已经在第 \(i\) 个素数的时候被筛尽了。而 \(g(p_i-1,i-1)\) 中只有只有 \([1,n]\) 中素数(除去 \(p_{i+1}\))外的贡献。
所以当 \(p_i\leq n\lt p_i^2\) 时,有
也就是说,我们发现 \(p_i^2 \gt n\) 时停止转移。记此时的 \(i=I\)。则
预处理出前缀和,我们将复杂度优化到了 \(\displaystyle \Theta\left(\frac{n^{\frac{3}{4}}}{\log n}\right)\)。
可以利用 Barrett 约减来快速取模,减小常数。
Min_25 筛
P5325 【模板】Min_25 筛
有积性函数 \(f(p^k)=p^{k+1}-p^k\)(\(p\in \mathbb{P}\))。求
\(n\leq 10^{10}\)。
实际上只需要考虑 \(f(p^k)=p^k\),求 \(\sum_{i=1}^n f(i)\) 的问题。下文以此为准。
设 \(p_i\) 为第 \(i\) 个素数,设完全积性函数 \(f'\) 在素数点值处与 \(f\) 相同(例如 \(f'(x)=x\) 或 \(f'(x)=x^2\))。定义
也就是最小素因数大于 \(p_j\) 的数,不难发现与上一题的 \(g(n,j)\) 本质相同,转移即为
\(g(p_i-1,i-1)\) 实际上就是前 \((i-1)\) 个素数的 \(1\)(或 \(2\))次方和,不难预处理。不妨记为 \(h(i)\)。
设 \(p_x\) 为满足 \(p_x^2\leq n\) 的最大的 \(x\)。下文我们记 \(g(n)=g(n,x)\)。
定义 \(\displaystyle S(n,j)=f(i)[\min_{p\in \mathbb{P},p\mid i} p\gt p_j]\),即最小素因数大于 \(p_j\) 的 \(i\) 的点值和。
对于 \(S(n,j)\),考虑 大于 \(p_j\) 的素数的贡献,即为 \(g(n)-h(j)\)。另外一个部分就是最小素因子大于 \(j\) 的合数,这里我们枚举最小素因子的次数。即
加上 \([c\neq 1]\) 是因为在 \(c\gt 1\) 时,\(p_k^c\) 没有被计入答案。(\(c=1\) 时已在素数处被计入)
最终答案为 \(S(n,0)+f(1)\)。
不需要记忆化。可以证明时间复杂度为 \(\displaystyle \Theta\left(\frac{n^{0.75}}{\log n}\right)\)。
DIVCNT2 - Counting Divisors (square)
求出
\(n\leq 10^{12}\),\(\texttt{20s}\)。对 \(2^{64}\) 取模。
不难发现,\(f(p^c)=2c+1\)。取 \(f'(p)=3\) 满足条件。
DIVCNT3 - Counting Divisors (cube) / DIVCNTK - Counting Divisors (general)
多倍经验。
注意,DIVCNT1 并不是 min_25 筛系列的(因为数据范围过大)。
LOJ #6235. 区间素数个数
求 \([1,n]\) 间的素数个数。\(n\le 10^{11}\)。
一个比较不优雅的做法是,直接拿 P5493 的代码改一下(令 \(k=0\),再取一个大于 \(10^{11}\) 的模数),是可以通过的(Record),但是太慢。
由于这个东西后面还要用,我们考虑把它改得优雅一点。
设 \(g(n,i)\) 为用前 \(i\) 个素数筛过了之后的 \(S(n)\)。初始时有 \(g(n,0)=i\)。
然后递推式就是
注意到 \(g(p_i-1,i-1)=i-1\)。
然后直接做就行了。
DIVFACT4 - Divisors of factorial (extreme)
求出 \(n!\) 的因数个数,对 \(m\) 取模。
\(n\le 10^{11}\),\(\texttt{20s}\)。
我们有经典结论。
引理
阶乘 \(n!\) 中,素数 \(p\) 的指数为
\[\sum_{i\ge 1, p^i\le n}\left\lfloor\frac{n}{p^i}\right\rfloor \]证明
\(i=1\) 的时候求出了有多少个数中含有 \(p\) 这个因子,所以贡献为 \(\left\lfloor\frac{n}{p}\right\rfloor\)。
\(i=2\) 时,\(\left\lfloor\frac{n}{p^2}\right\rfloor\) 求出了有多少个数中含有 \(p^2\) 这个因子。原本每个因子的贡献是 \(2\) 的,但是由于 \(p\) 的贡献已经在 \(i=1\) 的时候加上一部分了,所以我们只需要加上 \(p^2\) 的贡献即 \(\left\lfloor\frac{n}{p^2}\right\rfloor\) 就好了。
依此类推,正确性显然。
直接做是 \(\Theta(\frac{n}{\ln n}\cdot \ln n)=\Theta(n)\) 的。
然而,你发现,对于 \((\sqrt n, n]\) 范围内的素数,它的指数只会是 \(1\)。所以我们可以考虑根号平衡。
具体地,对于 \([2, \sqrt n]\) 内的素数,我们直接暴力求,这部分是 \(\Theta(\sqrt n)\) 的;对于 \((\sqrt n, n]\) 的素数,其实就是 LOJ #6235,这部分是 \(\displaystyle \Theta\left(\frac{n^{0.75}}{\log n}\right)\) 的。
所以我们在 \(\displaystyle \Theta\left(\frac{n^{0.75}}{\log n}\right)\) 内解决了本题。
XMAS19E Sum of f(n)
答案就是
算是上一题的双倍经验吧。
参考文献/推荐阅读
- OI中常用数论函数求和法的简化陈述 by negiizhao