关于线性筛及其扩展

关于线性筛及其扩展

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;
        }
    }
}

欧拉函数

思路

首先,我们知道欧拉函数表示为

\[\varphi(n)=n \times \prod_{i=1}^k (1-\frac{1}{p_i}) \]

其中 \(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=\prod^{k}_{i=1}p_i^{a_i}=p_1^{a_1}*p_2^{a_2} \dots p_k^{a_k} \]

\(n\)的因数个数

\[d_i=\prod_{i=1}^k (a_i+1) \]

证明

用到乘法原理

我不会! 自豪.jpg

思路

  1. \(i\) 为质数时,\(\textit{num}_i \gets 1,\textit{d}_i \gets 2\) (很显然对吧),同时设 \(q = \left\lfloor \dfrac {i}{p} \right\rfloor\),其中 \(p\)\(i\) 的最小质因子

  2. \(p\)\(q\) 的质因子时,

\[\textit{num}_i \gets \textit{num}_q + 1,\textit{d}_i \gets \dfrac{\textit{d}_q}{\textit{num}_i} \times (\textit{num}_i + 1) \]

  1. \(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];
        }
    }
}

结语

奖励自己一张图吧~

尾图

~~都看到这了,真的不点个赞吗(>ω<*)~~
posted @ 2024-06-13 17:35  Elaina_0  阅读(16)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end