「算法笔记」线性筛
一、Etratosthenes 筛法
任意整数 \(x\) 的倍数 \(2x,3x,\cdots\) 都不是质数。考虑从 \(2\) 开始,由小到大扫描每个数 \(x\),把它的倍数 \(2x,3x,\cdots,\lfloor \frac{n}{x}\rfloor \times x\) 标记为合数。当扫描到一个数时,若它尚未被标记,则它不能被 \(2\sim x-1\) 之间的任何数整除,该数就是质数。
另外,可以发现,\(2\) 和 \(3\) 都会把 \(6\) 标记为合数。实际上,小于 \(x^2\) 的 \(x\) 的倍数在扫描更小的数时就已经被标记过了。可以对 Etratosthenes 筛法进行优化,对于每个数 \(x\),只需要从 \(x^2\) 开始,把 \(x^2,(x+1)\times x,\cdots,\lfloor \frac{n}{x}\rfloor \times x\) 标记为合数即可。
memset(vis,0,sizeof(vis)); //合数标记 for(int i=2;i<=n;i++){ if(vis[i]) continue; p[++cnt]=i; //i 是质数 for(int j=i;j<=n/i;j++) vis[i*j]=1; }
时间复杂度:\(\mathcal O(n\log \log n)\)。
二、线性筛法
可以发现,即使在优化后(从 \(x^2\) 开始),Eratosthenes 筛法仍然会重复标记合数。如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 \(\mathcal O(n)\) 了。
每个数被它最小的质因子筛一次。
vis[0]=vis[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]) p[++cnt]=i; for(int j=1;j<=cnt&&i*p[j]<=n;j++){ vis[i*p[j]]=1; if(i%p[j]==0) break; } }
- 每个数被它的最小质因子筛掉。每次筛去 \(i\times p[j]\),\(p[j]\) 是这个数的最小质因子。
- 如果 \(i\mid p[j]\) 说明 \(i\times p[j]\) 有两个 \(p[j]\) 的质因子,并且 \(p[j]\) 是 \(i\times p[j]\) 的最小质因子。若再枚举,则 \(p[j]\) 就不是 \(i\times p[j]\) 的最小质因子了。所以筛过一次 \(p[j]\) 就 break。
三、筛积性函数
1. 一些定义
数论函数是指一个正整数到整数的映射。
积性函数:对于所有 互质 的整数 \(a,b\),有性质 \(f(ab)=f(a)f(b)\) 的数论函数。\(f(1)=1\)。
完全积性函数:对于所有整数 \(a,b\),有性质 \(f(ab)=f(a)f(b)\) 的数论函数。
2. 常见的积性函数
约数个数函数 \(d(n)=\sum_{d|n} 1\)
约数和函数 \(\sigma (n)=\sum_{d|n} d\)
约数 \(k\) 次幂函数 \(\sigma _k (n)=\sum_{d|n} d^k\)
欧拉函数 \(\varphi (n)=\sum_{i=1}^n [\gcd(i,n)=1]\)
莫比乌斯函数 \(\mu (n)=\begin{cases}1&{n=1}\\(-1)^k&c_{1,2,...,k}=1\ (n=\prod_{i=1}^k p_i^{c_i})\\0&c_i>1\end{cases}\)
四、常见线性筛
1. 求约数和
给定 \(n\),求 \(f(1),f(2)...f(n)\)。其中 \(f(x)=\sum_{d|x} d\)。
证明约数和函数是积性函数:
考虑 \(a,b\),并且 \(\gcd(a,b)=1,ab=x\)。
\(f(a)f(b)=\sum_{d\mid a}d\sum_{p\mid b}p=\sum_{d\mid a}\sum_{p\mid b}dp=\sum_{(dp)\mid (ab)} dp=\sum_{t\mid x}t=f(x)\)
其中,因为 \(\gcd(a,b)=1\),则 \(\gcd(d,p)=1\)。
线性筛求约数和函数:
根据唯一分解定理,可得:\(n=p_1^{c_1}\times p_2^{c_2}\times \cdots \times p_k^{c_k}=\prod\limits_{i=1}^k p_i^{c_i}\)。
\(\sigma (x)=(1+p_1+p_1^2+\cdots+p_1^{c_1})\times (1+p_2+p_2^2+\cdots p_2^{c_2})\times \cdots\times (1+p_k+p_k^2\cdots p_k^{c_k})\)
\(\sigma (x)=\prod\limits_{i=1}^k \sum\limits_{j=0}^{c_i} p_i^j\)。其中 \(\sigma (x)\) 表示 \(x\) 的约数和。
令 \(f_i\) 表示 \(i\) 的约数和,\(g_i\) 表示 \(1+p+p^2+\cdots +p^c\),其中 \(p\) 表示 \(i\) 的最小质因子。
g[1]=f[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]) p[++cnt]=i,g[i]=f[i]=i+1; //当 i 是质数时,显然有 g[i]=f[i]=i+1 for(int j=1;j<=cnt&&i*p[j]<=n;j++){ vis[i*p[j]]=1; int x=i*p[j]; if(i%p[j]==0){ g[x]=g[i]*p[j]+1,f[x]=f[i]/g[i]*g[x]; //多了一个因子,也就是 1+p^1+p^2+...+p^c 变成了 1+p^1+p^2+...+p^{c+1} 了,那么更新 g 只需要将所有的乘上 p 再 +1 就好了。 break; } else f[x]=f[i]*f[p[j]],g[x]=1+p[j]; //新的因子(i*p[j] 里原先没有 p[j] 这一项) 。p[j] 是 i*p[j] 的最小质因子。 } } for(int i=1;i<=n;i++) f[i]=f[i-1]+f[i];
2. 求欧拉函数
对于正整数 \(n\),欧拉函数是小于或等于 \(n\) 的正整数中与 \(n\) 互质的数的数目,记作 \(\varphi(n)\)。
\(\varphi(n)=\sum_{i=1}^n \ [\gcd(i,n)=1]\)
\([a]\):如果 \(a\) 为真,则 \([a]\) 的值为 \(1\);否则为 \(0\)。
设 \(n=\prod_{i=1}^k{p_i}^{c_i}\)(\(p_i\) 是质数)。
公式:\(\varphi(n)=n\prod_{i=1}^k(1-\frac{1}{p_i})\)。
vis[0]=vis[1]=1,phi[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]) p[++cnt]=i,phi[i]=i-1; //当 i 为质数时,显然有 phi[i]=i-1。 for(int j=1;j<=cnt&&i*p[j]<=n;j++){ vis[i*p[j]]=1; if(i%p[j]==0){ phi[i*p[j]]=phi[i]*p[j]; //i 和 i*p[j] 都包含 p[j] 这个质因子。将 phi[i*p[j]] 和 phi[i] 按照公式写出,二者相除,商为 p[j]。 break; } phi[i*p[j]]=phi[i]*phi[p[j]]; //i 和 p[j] 互质。利用积性函数的性质。 } }
3. 求莫比乌斯函数
\(p_i\) 是质数。\(\mu (n)=\begin{cases}1&{n=1}\\(-1)^k&c_{1,2,...,k}=1\ (n=\prod_{i=1}^k p_i^{c_i})\\0&c_i>1\end{cases}\)
证明莫比乌斯函数是积性函数:
对于 \(\forall a,b\),并且 \(\gcd(a,b)=1,ab=x\)。\(\mu(a)\mu(b)\):
1. 若 \(\mu(a)=0\) 或 \(\mu(b)=0\),则 \(ab\) 必含平方因子(即存在 \(c_i>1\))。所以 \(\mu(a,b)=0\)。
2. 否则,由于 \(\gcd(a,b)=1\),则 \(ab\) 不含平方因子。则 \(\mu(a)\mu(b)=(-1)^{k_1}(-1)^{k_2}=(-1)^{k_1+k_2}=\mu(ab)\)。
vis[0]=vis[1]=1,u[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]) p[++cnt]=i,u[i]=-1; //当 i 为质数时,此时 k=1 且 c1=1,显然有 u[i]=-1 for(int j=1;j<=cnt&&i*p[j]<=n;j++){ vis[i*p[j]]=1; if(i%p[j]==0){u[i*p[j]]=0;break;} //多了一个因子。此时 i*p[j] 必含平方因子,所以 u[i*p[j]]=0。 u[i*p[j]]=-u[i]; //新的因子。 } }
4. 求约数个数
根据唯一分解定理,可得:\(n=p_1^{c_1}\times p_2^{c_2}\times \cdots \times p_k^{c_k}=\prod\limits_{i=1}^kp_i^{c_i}\)。
\(p_i^{c_i}\) 的约数有 \(p_i^0,p_i^1,\cdots,p_i^{c_i}\) 共 \(c_i+1\) 个,根据乘法原理,可得 \(n\) 的约数个数为 \(d(n)=\prod\limits_{i=1}^k (c_i+1)\)
令 \(d_i\) 表示 \(i\) 的约数个数,\(g_i\) 表示 \(i\) 的最小质因数的次数。
vis[0]=vis[1]=1,d[1]=1; for(int i=2;i<=n;i++){ if(!vis[i]) p[++cnt]=i,d[i]=2,g[i]=1; //若 i 为质数,显然有 d[i]=2,g[i]=1 for(int j=1;j<=cnt&&i*p[j]<=n;j++){ vis[i*p[j]]=1; if(i%p[j]==0){ d[i*p[j]]=d[i]/(g[i]+1)*(g[i]+2); //多了一个因子。并且 p[j] 是 i*p[j] 的最小质因子。d[i*p[j]]=(1+c1+1)(1+c2)...(1+ck)。 g[i*p[j]]=g[i]+1; break; } d[i*p[j]]=d[i]*d[p[j]],g[i*p[j]]=1; //新的因子。i*p[j] 之前不包含 p[j]。p[j] 是 i*p[j] 的最小质因子。 } }
四、一般线性筛
一个积性函数 \(f\),可以考虑以下的线性筛法:
-
要计算 \(f(n)\),考虑到 \(n=\prod\limits_{i=1}^k p_i^{c_i}\)。
-
在线性筛的过程中,如果 \(i\bmod p[j]\neq 0\),就有 \(f(i\cdot p[j])=f(i)f(p[j])\)。
-
否则,考虑 \(k\mid (i\cdot p[j]),\gcd(k,p[j])=1\),则有 \(f(i\cdot p[j])=f(k)f(\frac{i\cdot p[j]}{k})\)。
-
因此,我们只需要记录对于每一个 \(i\cdot p[j]\) 对应的 \(k\),或者推导出 \(f(p^c)\) 到 \(f(p^{c+1})\) 的关系式即可。