算法学习笔记(24):筛法

筛法

埃氏筛

考虑埃氏筛是通过把每个质数的倍数都标记为合数, 这样剩下的数就都是质数。

时间复杂度 \(O(nloglogn)\)

bitset<10000000> p; 
void Eratosthenes(int n) {
	for(int i = 2; i <= n; i++) {
		if(!p[i]) prime[++tot] = i;
		for(int j = i << 1; j <= n; j += i) 
			p.set(j, true);
	}
}

虽然时间复杂度高于线性, 但是搭配 bitset 效率会比欧拉筛还要优秀!

欧拉筛

欧拉筛时间复杂度 \(O(n)\), 具体就是每个合数只会被其最小质因子筛一次。

bool p[10000000];
void Euler(int n) {
	for(int i = 2; i <= n; i++) {
		if(!p[i]) prime[++tot] = i;
		for(int j = 1; j <= tot && i * prime[j] <= n; j++) {
			p[i * prime[j]] = 1;
			if(i % prime[j] == 0) break;
		}
	}
}

看起来很神奇, 两层 for 循环时间复杂度 \(O(n)\)

正确性证明:

考虑每个合数可以表示成 \(a = b * p1\)\(p1\) 就是 \(a\) 的最小质因子, 考虑 \(b\) 一定会筛到 \(a\), 因为 \(p1\) 一定也是 \(a\) 的最小质因子, 所以在 break 前一定会筛到 \(a\)

时间复杂度证明:

考虑每个合数可以表示成 \(a = b * p1\)\(p1\) 就是 \(a\) 的最小质因子, 考虑拿 \(b\) 去筛的时候只会筛到 \(a\) 一次, 因为我们枚举的质数是递增的。
现在我们就是证明是否存在一个 \(c\)\(a = c * p2\)\(c\) 会筛到 \(a\), 考虑 \(p2\) 一定大于 \(p1\), 因为 \(p1\) 是最小质因子。 并且 \(p1\) 一定是 \(c\) 的因子, 所以在我们枚举质数时枚举到 \(p1\) 时候就会 break 掉, 所以不会筛到 \(a\)

欧拉函数

首先给出公式:

\(\large \phi(n) = n\prod \frac{p_i - 1}{p_i}\)

考虑用线性筛。
\(n \ mod \ p == 0\) 时, \(n\) 包含了 \(n * p\)
\( \begin{aligned} \phi(n) & = n \times \prod_{i = 1}^s{\frac{p_i - 1}{p_i}} \\\\ & = p_1 \times n' \times \prod_{i = 1}^s{\frac{p_i - 1}{p_i}} \\\\ & = p_1 \times \varphi(n') \end{aligned} \)

\(n \ mod \ p != 0\) 时, 则 \(n\)\(p\) 互质

\( \begin{aligned} \phi(n * p) &= \phi(p) * \phi(n) \\\\ &= (p - 1) * \phi(n) \\\\ \end{aligned} \)

代码:
注意 \(\phi[1] = 1\)

点击查看代码
bool p[10000000];
void Euler(int n) {
	phi[1] = 1;
	for(int i = 2; i <= n; i++) {
		if(!p[i]) prime[++tot] = i, phi[i] = i - 1;
		for(int j = 1; j <= tot && i * prime[j] <= n; j++) {
			p[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				phi[i * prime[j]] = prime[j] * phi[i];
				break;
			}
			else 
				phi[i * prime[j]] = (prime[j] - 1) * phi[i];
		}
	}
}

莫比乌斯函数

定义如下:
\(\mu(n)= \begin{cases} 1&n=1\\ 0&n\text{ 含有平方因子}\\ (-1)^k&k\text{ 为 }n\text{ 的本质不同质因子个数}\\ \end{cases}\)

在用欧拉筛时则有:
\( \mu(n)= \begin{cases} 0 & n' \bmod p_1 = 0\\\\ -\mu(n') & \text{otherwise} \end{cases} \)
证明:

\(i \ mod \ p == 0\) 时, \(i * p\) 就含有平方因子p, 所以 \(\mu(i * p) = 0\)

\(i \ mod \ p != 0\) 时, \(\mu(i * p)\) 就等于 \(-\mu(i)\), 由定义可得。

代码:

注意 \(\mu[1] = 1\)

点击查看代码
bool p[10000000];
void Euler(int n) {
	mu[1] = 1;
	for(int i = 2; i <= n; i++) {
		if(!p[i]) prime[++tot] = i;
		for(int j = 1; j <= tot && i * prime[j] <= n; j++) {
			p[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			}
			else 
				mu[i * prime[j]] = -mu[i];
		}
	}
}

约数个数函数

\(d(i * p) = 2d(i) - d(\frac{i}{p})\)

约数和函数

$ \sigma(i * p) = p(\sigma(i) - \sigma(\frac{i}{p})) + \sigma(i) $

其他情况就拿通项公式和积性函数算即可。

posted @ 2024-06-08 14:44  qqrj  阅读(26)  评论(0编辑  收藏  举报