关于线性筛及其扩展
关于线性筛及其扩展
xjb讲
顾名思义,是一种复杂度为线性的筛法
常用来求素数
不过她的扩展用法也是蛮多的
比如她可以用来求大多数积性函数 (当然,你要会,才能求)
积性函数
积性函数指对于所有互质的整数a和b有性质f(ab)=f(a)f(b)的数论函数
即,对于一个数论函数\(f(x)\),\(\forall gcd(a,b)=1\),满足\(f(a*b)=f(a)*f(b)\),则称\(f(x)\)具有积性
常见的积性函数:欧拉函数\(φ\),莫比乌斯函数\(μ\),\(gcd(n,k)(k为给定整数)\)
原理
首先积性函数 \(f\) 满足:对于任意质数 \(p\) 和正整数 \(k\),可以在 \(O(1)\) 时间内计算 \(f(p^k)\),那么可以在 \(O(n)\) 时间内筛出 \(f(1),f(2),\dots,f(n)\) 的值
在原有线性筛中额外求一个数组\(h\),\(h[i]\)表示\(i\)的最小质因子的幂次
即:\(x=p_1^{a_1}+p_2^{a_2}+ \dots,(a_i \neq 0,p_1 < p_2 < \dots)\)则\(h[x]=p_1^{a_1}\)
在线性筛结束之后,从\(1 \sim n\)线性递推,由\(gcd(h[i],i/h[i])=1\),\(f(i)=f(h[i])*f(i/h[i])\)
当 \(i=p^k\) 时需要特殊处理,一般从该积性函数的定义就可以直接计算
例:\(φ( p^k )=p^k - p^{k-1}\)
至此,我们便实现了线性筛的扩展
线性筛(欧拉筛)求素数
思路
也是很简单的:保证每个数只被它最小的因数筛去一次
code
Elaina's code
int prime[N]; // 保存素数
bool is_prime[N];
// 筛选 n 以内的所有素数
void xxs(int n){
memset(is_prime,1,sizeof(is_prime));
is_prime[0]=is_prime[1]=0; // 0和1都不是素数
for(int i=2;i<=n;++i){
if(is_prime[i]){// 如果i是素数
prime[++prime[0]]=i;
}
for(int j=1;j<=prime[0]&&i*prime[j]<=n;++j){
is_prime[i*prime[j]]=0;
// 如果i中包含了该质因子,则停止
if(i%prime[j]==0) break;
}
}
}
欧拉函数
思路
首先,我们知道欧拉函数表示为
其中 \(p_1, p_2, \dots ,p_k\) 为 \(n\) 的所有质因数,\(n\)为正整数
注意到在线性筛中,每一个合数都是被最小的质因子筛掉
比如设 \(p_1\) 是 $n4 的最小质因子,
\(n' = \frac{n}{p_1}\),那么线性筛的过程中 \(n\) 通过 \(n' \times p_1\) 筛掉
观察线性筛的过程,我们还需要处理两个部分,下面对 \(n' \bmod p_1\) 分情况讨论
-
如果 \(n' \bmod p_1 = 0\),那么 \(n'\) 包含了 \(n\) 的所有质因子
\[\begin{aligned} \varphi(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' \bmod p_1 \neq 0\),这时 \(n'\) 和 \(p_1\) 是互质的,根据欧拉函数性质,我们有:
\[\begin{aligned} \varphi(n) & = \varphi(p_1) \times \varphi(n') \\\\ & = (p_1 - 1) \times \varphi(n') \end{aligned} \]
code
Elaina's code
int n,phi[N],prime[N],cnt;
bool pri[N];
void Euler(){
mst(pri,1);
phi[1]=1;
for(int i=2;i<=n;i++){
if(pri[i]){
prime[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt;j++){
int k=i*prime[j];
if(k>n){
break;
}
pri[k]=0;
if(i%prime[j]==0){
phi[k]=prime[j]*phi[i];
break;
}else{
phi[k]=(prime[j]-1)*phi[i];
}
}
}
}
莫比乌斯函数
学了再说吧 Ciallo~(∠・ω< )⌒☆
筛法求约数个数
前置
用 \(d_i\) 表示 \(i\) 的约数个数,\(num_i\) 表示 \(i\) 的最小质因子出现次数
约数个数定理
由唯一分解定理,
对于一个大于\(1\)正整数\(n\)可以分解质因数:
则\(n\)的因数个数
证明
用到乘法原理
我不会! 自豪.jpg
思路
-
当 \(i\) 为质数时,\(\textit{num}_i \gets 1,\textit{d}_i \gets 2\)
(很显然对吧),同时设 \(q = \left\lfloor \dfrac {i}{p} \right\rfloor\),其中 \(p\) 为 \(i\) 的最小质因子 -
当 \(p\) 为 \(q\) 的质因子时,
- 当 \(p,q\) 互质时,$$\textit{num}_i \gets 1,\textit{d}_i \gets \textit{d}_q \times (\textit{num}_i+1)$$
code
Elaina's code
int prime[N];
int d[N],num[N];
bool pri[N];
void pre(int n){
d[1]=1;
mst(pri,1);
pri[0]=pri[1]=0;
for(int i=2;i<=n;++i){
if(pri[i]){
prime[++prime[0]]=i;
d[i]=2;
num[i]=1;
}
for(int j=1;j<=prime[0]&&i*prime[j]<=n;++j){
pri[i*prime[j]]=0;
if(i%prime[j]==0){
num[i*prime[j]]=num[i]+1;
d[i*prime[j]]=d[i]/num[i*prime[j]]*(num[i*prime[j]]+1);
break;
}
num[i*prime[j]]=1;
d[i*prime[j]]=d[i]*2;
}
}
}
筛法求约数和
这个就比较简单了
直接看码
设 \(f_i\) 表示 \(i\) 的约数和,\(g_i\) 表示 \(i\) 的最小质因子的 \(p^0+p^1+p^2+\dots p^k\)
Elaina's code
int prime[N];
int g[N],f[N];
bool pri[N];
void pre(int n){
g[1]=f[1]=1;
mst(pri,1);
pri[0]=pri[1]=0;
for(int i=2;i<=n;++i){
if(pri[i]){
prime[++prime[0]]=i;
g[i]=i+1;
f[i]=i+1;
}
for(int j=1;j<=prime[0]&&i*prime[j]<=n;++j){
pri[i*prime[j]]=0;
if(i%prime[j]==0){
g[i*prime[j]]=g[i]*prime[j]+1;
f[i*prime[j]]=f[i]/g[i]*g[i*prime[j]];
break;
}
f[i*prime[j]]=f[i]*f[prime[j]];
g[i*prime[j]]=1+prime[j];
}
}
}
结语
奖励自己一张图吧~