关于线性筛及其扩展

关于线性筛及其扩展

xjb

顾名思义,是一种复杂度为线性的筛法
常用来求素数
不过她的扩展用法也是蛮多的
比如她可以用来求大多数积性函数 (当然,你要会,才能求)

积性函数

积性函数指对于所有互质的整数a和b有性质f(ab)=f(a)f(b)的数论函数

即,对于一个数论函数f(x)gcd(a,b)=1,满足f(ab)=f(a)f(b),则称f(x)具有积性

常见的积性函数:欧拉函数φ,莫比乌斯函数μgcd(n,k)k

原理

首先积性函数 f 满足:对于任意质数 p 和正整数 k,可以在 O(1) 时间内计算 f(pk),那么可以在 O(n) 时间内筛出 f(1),f(2),,f(n) 的值

在原有线性筛中额外求一个数组hh[i]表示i的最小质因子的幂次

即:x=p1a1+p2a2+(ai0,p1<p2<)h[x]=p1a1

在线性筛结束之后,从1n线性递推,由gcd(h[i],i/h[i])=1f(i)=f(h[i])f(i/h[i])

i=pk 时需要特殊处理,一般从该积性函数的定义就可以直接计算

例:φ(pk)=pkpk1

至此,我们便实现了线性筛的扩展

线性筛(欧拉筛)求素数

思路

也是很简单的:保证每个数只被它最小的因数筛去一次

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

欧拉函数

思路

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

φ(n)=n×i=1k(11pi)

其中 p1,p2,,pkn 的所有质因数,n为正整数

注意到在线性筛中,每一个合数都是被最小的质因子筛掉

比如设 p1 是 $n4 的最小质因子,

n=np1,那么线性筛的过程中 n 通过 n×p1 筛掉

观察线性筛的过程,我们还需要处理两个部分,下面对 nmodp1 分情况讨论

  • 如果 nmodp1=0,那么 n 包含了 n 的所有质因子

    φ(n)=n×i=1spi1pi=p1×n×i=1spi1pi=p1×φ(n)

  • 如果 nmodp10,这时 np1 是互质的,根据欧拉函数性质,我们有:

    φ(n)=φ(p1)×φ(n)=(p11)×φ(n)

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~(∠・ω< )⌒☆

筛法求约数个数

前置

di 表示 i 的约数个数,numi 表示 i 的最小质因子出现次数

约数个数定理

由唯一分解定理,
对于一个大于1正整数n可以分解质因数:

n=i=1kpiai=p1a1p2a2pkak

n的因数个数

di=i=1k(ai+1)

证明

用到乘法原理

我不会! 自豪.jpg

思路

  1. i 为质数时,numi1,di2 (很显然对吧),同时设 q=ip,其中 pi 的最小质因子

  2. pq 的质因子时,

numinumq+1,didqnumi×(numi+1)

  1. p,q 互质时,numi1,didq×(numi+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;
        }
    }
}

筛法求约数和

这个就比较简单了

直接看码

fi 表示 i 的约数和,gi 表示 i 的最小质因子的 p0+p1+p2+pk

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 @   Elaina_0  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示