筛法(埃氏/欧拉/杜教/PN筛)
筛法
概述
一般的筛法可以筛出某个范围内的素数。
用恰当的筛法可以筛出具有某种性质的函数,或某种函数的前缀和。
埃氏筛
对于每一个素数 \(p\),标记 \(kp+ p^2,k\ge0\) 为合数。
正确性:假设 \((p,p^2)\) 内存在一个含有p这个因子的合数没被标记,那它一定可以被写为 \(pxq,x\in z, q\) 是质数的形式。
因为 \(pxq\) 小于 \(p^2\),所以 \(q \le p\),所以这个数会被 \(q\) 标记,假设不成立。
所以可以每次遇到一个不是合数的数,就认为它是素数。
附上代码:
void ash_shai(int MAX){
ll i = 2; for(; i * i <= MAX; ++i) if(!vis[i]) {
pri.push_back(i);
for(ll j = i * i; j <= MAX; j += i) vis[j] = true;
}
}
复杂度 \(O(n\log(\log n))\) 并且常数很小,所以可以近似看作 \(O(n)\)。OI-wiki上好像给出了一些神秘优化方法,不过我没看懂。
这个算法一般用于只需要筛素数的题。
欧式筛
又称线性筛。因为是严格 \(O(n)\) 的。
用这个算法主要是为了筛函数值。
对于合数,我们希望只用这个数的最小质因子把它筛掉。
我们对于每一个数\(i\),枚举小于它并与其互质的质数 \(p\),把由它俩乘积的这个合数打上标记。
正确性:我们认为这些合数是被 \(p\) 标记的。如果 p,i 不互质,那么该合数的最小质因子是 \(i\over p\) ,或 \(i\over p\) 的因子。如果 p 是合数,那么 \(ip\) 的最小质因子应该是 \(p\) 的因子。因为 \(p\lt i\),且 \(i\) 没有小于 \(p\) 的因子,所以 \(p\) 就是 \(ip\) 的最小质因子。
每次遇到一个不是合数的数,就认为它是素数。
不过更重要的是用欧拉筛去筛函数值。
可以被欧拉筛筛出来的函数必须满足以下性质:
对于 \(k = 1, k \in prime, k \mid p\), \(f(pk)\) 可以由 \(f(p)\) 和 \(f(k)\) 快速得到,并且知道 \(f(1)\) 的值。
所以大部分的积性函数都可以用欧拉筛线性地筛出函数值。
例如 \(\varphi\) 和 \(\mu\):
\(\varphi(1) = \mu(1) = 1\)
\(\varphi(p) = p - 1, \mu(p) = 1\)
\(\varphi(pk) = \varphi(p)*\varphi(k) = (k-1)\varphi(p),\mu(pk) = \mu(p)*\mu(k) = -1\mu(p)\),\(k\) 是质数,且 k,p 互质
\(\varphi(pk) = k\varphi(p),\mu(pk) = 0,k \in prime, k\mid p\)
代码:
void get_oula(int M){
phi[1] = 1, mu[1] = 1;
for(int i=2;i<=M;++i){
if(!vis[i]) pri.push_back(i), phi[i] = i - 1, mu[i] = -1;//k=1
for(int j : pri) {//k is prime
if(i*j>M) break;
vis[i*j] = 1;
phi[i*j] = phi[i] * (j - 1);
mu[i*j] = mu[i] * (-1);
if(i % j == 0){//k | p
phi[i*j] = phi[i] * j;
mu[i*j] = 0;
break;
}
}
}
for(int i=1;i<=M;++i){
sp[i] = sp[i - 1] + phi[i];
sm[i] = sm[i - 1] + mu[i];
}
}
杜教筛
缺点:只能得到函数的部分函数值的前缀和。对要筛的函数有很多限制。
优点:1. 复杂度 \(O(n^{2\over 3})\).2. 可以得到前缀和。
正文
对于数论函数 \(f\),计算 \(S(n)=\sum_{i=1}^{n}f(i)\)。
需要找到一个合适的函数 \(g\),来和 \(f\) 进行卷积。
如果 \(g(1),\sum_i^n(f\otimes g)(i),\sum^{n}_{d=2}g(d)\) 这三个都是能快速求到的,那么这似乎就可以用整除分块来递归地做。
最后一个问题:这个是什么时间复杂度?
观察到:当\(a,b\gt0\) 且 \(b\in \Z\) 时, \(\left\lfloor\frac{\lfloor\frac na\rfloor}{b}\right\rfloor=\left\lfloor\frac{n}{ab}\right\rfloor\),所以无论怎么递归,S 会计算到的数只在集合 \(\{\left\lfloor\frac nx\right\rfloor\}\) 中,只有 \(O(\sqrt n)\) 个。用微积分的知识,可以得到时间复杂度是 \(O(n^{3\over4})\)
不过我们还可以通过暴力预处理(欧拉筛)前面的 \(2\over3\),使时间复杂度降到 \(O(n^{2\over3})\).
最大的问题在与要有合适的 \(g\)。我们发现 常数函数 \(1\) 对 \(\varphi\) 和 \(\mu\) 就满足此性质。
把 \(\mu\)带到我们的式子里:
即:
\(\operatorname{1}(i)\) 的前缀和就是 \(i\)。杜教筛时间复杂度 \(O(n^{\frac{2}{3}})\)
对于 \(\varphi\) 带到我们的式子里:
即:
\(\operatorname{1}(i)\) 的前缀和就是 \(i\)。杜教筛时间复杂度 \(O(n^{\frac{2}{3}})\)
我们还发现 \(\mu·id_k\) 和 \(\varphi·id_k\) 可以找到 \(id_k\) 作为它们的函数 \(g\)。(注:这里的点乘表示的是对应位置相乘,即 :\((f·g)(n) = f(n)g(n)\))
Powerful Number筛
这玩意相比杜教筛,优点是对 \(g\) 的限制更少,可以处理更积性的 \(f\),缺点是写起来更史。
Powerful Number
定义:对于正整数 \(n\),记 \(n\) 的质因数分解为 \(n = \prod_{i=1}^{m} p_{i}^{e_{i}}\)。\(n\) 是 Powerful Number(简称 PN) 当且仅当 \(\forall 1 \le i \le m, e_{i} > 1\)。
PN 有如下性质:
-
所有 PN 都可以表示成 \(a^{2}b^{3}\) 的形式。
证明:若 \(e_i\) 是偶数,则将
\(p_{i}^{e_{i}}\) 合并进 \(a^{2}\) 里;若 \(e_i\) 为奇数,则先将 \(p_{i}^{3}\) 合并进 \(b^{3}\) 里,再将 \(p_{i}^{e_{i}-3}\) 合并进 \(a^{2}\) 里。 -
\(n\) 以内的 PN 有 \(O(\sqrt{n})\) 个。
证明:考虑枚举 \(a\),再考虑满足条件的 \(b\) 的个数,有 PN 的个数约等于
那么如何求出 \(n\) 以内所有的 PN 呢?线性筛找出 \(\sqrt{n}\) 内的所有素数,再 DFS 搜索各素数的指数即可。由于 \(n\) 以内的 PN 至多有 \(O(\sqrt{n})\) 个,所以至多搜索 \(O(\sqrt{n})\) 次。
Powerful Number筛要求存在一个函数 \(g\) 满足:
-
\(g\) 是积性函数。
-
\(g\) 易求前缀和。
-
对于质数 \(p\),\(g(p) = f(p)\)。
假设现在要求积性函数 \(f\) 的前缀和.
首先,构造出一个易求前缀和的积性函数 \(g\),且满足对于素数 \(p\),\(g(p) = f(p)\)。记 \(G(n) = \sum_{i=1}^{n} g(i)\)。
然后,设函数 \(h\) 满足 \(f = g \otimes h\),根据狄利克雷卷积的性质可以得知 \(h\) 也为积性函数,因此 \(h(1) = 1\)。
对于素数 \(p\),\(f(p) = g(1)h(p) + g(p)h(1) = h(p) + g(p) \implies h(p) = 0\)。根据 \(h(p)=0\) 和 \(h\) 是积性函数可以推出对于非 PN 的数 \(n\) 有 \(h(n) = 0\),即 \(h\) 仅在 PN 处取有效值。这是保证 PN 筛时间复杂度的基本原理。
现在,根据 \(f = g \ast h\) 有:
\(O(\sqrt{n})\) 找出所有 PN,计算出所有 \(h\) 的有效值。对于 \(h\) 有效值的计算,只需要计算出所有 \(h(p^c)\) 处的值,就可以根据 \(h\) 为积性函数推出 \(h\) 的所有有效值。
现在对于每一个有效值 \(d\),计算
\(h(d)G\left(\left\lfloor \dfrac{n}{d} \right\rfloor\right)\) 并累加即可得到 \(F(n)\)。
下面考虑计算 \(h(p^c)\),一共有两种方法:
- 直接推出 \(h(p^c)\) 仅与 \(p,c\) 有关的计算公式,再根据公式计算 \(h(p^c)\)
- 根据 \(f = g \ast h\) 有 \(f(p^c) = \sum_{i=0}^c g(p^i)h(p^{c-i})\),移项可得 \(h(p^c) = f(p^c) - \sum_{i=1}^{c}g(p^i)h(p^{c-i})\),现在就可以枚举素数 \(p\) 再枚举指数 \(c\) 求解出所有 \(h(p^c)\)。
复杂度 \(O(\sqrt n\log n)\),而且这个上界比较宽松。
可惜的是,我们没有 PN 筛的模板题。不过 Min_25 筛的模板题可以用 PN 筛来做。
Min_25筛
咕咕咕
洲阁筛
咕咕咕

浙公网安备 33010602011771号