浅谈筛法

浅谈筛法

Euler 筛

P3601 签到题

P4626 一道水题 II

Eratosthenes 筛

可以证明(不是“不难证明”),Eratosthenes 筛的复杂度为 \(\Theta(n\log \log n)\)

Eratosthenes 筛的复杂度证明

Dirichlet 前缀和

P5495 【模板】Dirichlet 前缀和 为例。

给定 \(a_1,a_2,\cdots,a_n\),求 \(b_1,b_2,\cdots,b_n\),满足

\[b_i=\sum_{d\mid i} a_d \]

\(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\)。可以任意重排数列,最大化

\[\sum_{i=1}^n \gcd(a_1,a_2,\cdots,a_i) \]

的值。\(n\leq 2\times 10^5\)\(1\leq a_i\leq 2\times 10^7\)

我们有一些性质。

  • 值相同的数一定放在一起。
    证明显然。
  • 最优方案中,数列一定不增。
    证明显然。

\(cnt_i\)\(a_i\)\(i\) 的倍数个数,设 \(f[i]\) 为重排后的数组最后一个数含有因数 \(i\) 的最大权值。我们有

\[f[i]=\max(f[i\cdot p_j]+i\cdot (cnt_i-cnt_{i\cdot p_j})) \]

我们将 \(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\) 的前缀和拆开。

\[\begin{aligned} \sum_{i=1}^n f(i)&=\sum_{i=1}^n \sum_{d\mid i}g\left(\frac{i}{d}\right)h(d) \\ &=\sum_{d=1}^n h(d)\sum_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor} g(i) \\ &=\sum_{d=1}^n h(d)s(\left\lfloor\frac{n}{d}\right\rfloor)\\ &=h(1)s(n)+\sum_{d=2}^n h(d)s(\left\lfloor\frac{n}{d}\right\rfloor)\\ \end{aligned} \]

于是我们得到杜教筛的公式。

\[s(n)=\frac{\sum_{i=1}^n f(i)-\sum_{i=2}^n h(i)s(\left\lfloor\frac{n}{i}\right\rfloor) }{h(1)} \]

\(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\)。求

\[\sum_{i=1}^{\left\lfloor\sqrt{n}\right\rfloor} i^2S\left(\left\lfloor\frac{n}{i}\right\rfloor\right) \]

\(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)\) 求出(如果读者不会,可以阅读我写的《多项式基础》博客)。

考虑递推,我们先给出结论。

\[g(n,i)= \begin{cases} g(n,i-1)-p_i^k\left[g\left(\left\lfloor\dfrac{n}{p_i}\right\rfloor,i-1\right)-g(p_i-1,i-1)\right], & p_i^2\leq n\\ g(n,i-1) & p_i^2\gt n \end{cases} \]

引理
合数 \(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\) 时,有

\[g(n,i)=g(n,i-1)-p_i^k \]

也就是说,我们发现 \(p_i^2 \gt n\) 时停止转移。记此时的 \(i=I\)。则

\[\forall i\gt I, g(n,i)=g(n,I)-\sum_{j=I+1}^{i} p_j^k \]

预处理出前缀和,我们将复杂度优化到了 \(\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}\))。求

\[\sum_{i=1}^n f(i) \]

\(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\))。定义

\[g(n,j)=\sum_{i=1}^n f'(i)[i\in \mathbb{P} \text{ or } \min_{p\in \mathbb{P},p\mid i} p\gt p_j] \]

也就是最小素因数大于 \(p_j\) 的数,不难发现与上一题的 \(g(n,j)\) 本质相同,转移即为

\[g(n,i)= \begin{cases} g(n,i-1)-f'(p_i)(g(\left\lfloor\dfrac{n}{p_i}\right\rfloor,i-1)-g(p_i-1,i-1)), & p_i^2\leq n\\ g(n,i-1) & p_i^2\gt n \end{cases} \]

\(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\) 的合数,这里我们枚举最小素因子的次数。即

\[S(n,j)=g(n)-h_{j}+\sum_{k\gt j}\sum_{p_k^c\leq n} f(p_k^c)\left\{S\left(\left\lfloor\frac{n}{p_k^c},k\right\rfloor\right)+[c\neq 1]\right\} \]

加上 \([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)

求出

\[\sum _{i=1}^n \sigma_0(i^2) \]

\(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(n,i)= \begin{cases} g(n,i-1)-\left[g\left(\left\lfloor\dfrac{n}{p_i}\right\rfloor,i-1\right)-g(p_i-1,i-1)\right], & p_i^2\leq n\\ g(n,i-1) & p_i^2\gt n \end{cases} \]

注意到 \(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)

答案就是

\[\sum_{p\in \mathbb{P},p\le n} \sum_{i\ge 1,p^i\le n}\left\lfloor\frac{n}{p^i}\right\rfloor \]

算是上一题的双倍经验吧。

参考文献/推荐阅读

posted @ 2024-02-20 20:59  Starrykiller  阅读(40)  评论(0)    收藏  举报