线性筛常见办法

线性筛总结

意在说明线性筛的一般方法和介绍几种作者遇到过的可以线性筛的数论函数。

最基础的线性筛

首先我们必须要知道以下线性筛的最基础的知识,要知道每一个数都是被其最小质因子唯一筛掉的,线性筛的过程其实是在处理最小质因子!

如果不会最基础的线性筛的原理,请转模板题题解。

一些前置小知识

常见数论函数

\(1.\) 常函数:\(I(n)=1\)

\(2.\) 恒等函数:\(id(n)=n\)

\(3.\) 单位函数:\(\varepsilon(n)=[n=1]\) 又叫做单位元

(以上都是完全积性函数)

\(4.\) 欧拉函数:\(\varphi(n)\)\(n\) 互质的正整数个数

\(5.\) 除数函数:\(\sigma_x(n)\) \(n\)\(x\) 次幂因子和,特殊情况是 \(\sigma_0(n)\)\(\sigma_1(n)\) ,分别代表因数个数和因数和,常写作 \(\sigma(n)\)\(d(n)\)

\(6.\) 莫比乌斯函数:\(\mu(n)\) 请见本文定义

(以上都是积性函数)

\(7.\) 素因数个数函数(prime factor numbers function)\(\Omega(n)\) :表示 \(n\) 的素因子个数(也有写作 \(\omega\) 的?)

狄利克雷卷积的定义和性质

详细请见作者的莫比乌斯反演总结部分,暂不赘述。

在这里也用处不大,主要是可以用来判断两个数论函数的卷积的一些性质。(比如如果两个都是积性函数,那么它们的卷积也是积性函数)

扩展

如果想具体了解以上的一点知识,可以去康康狄利克雷卷积总结

一般的线性筛法

分析

先直接给出一般的线性筛代码及其要求:

首先,我们要求函数是个积性函数,或者可以快速地通过 \(f(n),f(m)\) 得到 \(f(nm)\)\(\gcd(n,m)=1\)) ,这个快速就是会在最后线性筛的复杂度上乘上这个复杂度。

接下来,我们需要可以快速知道以下三个值:

\(1.\) \(f(1)\)

\(2.\) $f(p)\ \ p\ is \ prime $

\(3.\) \(f(p^k)\ \ \ p\ is \ prime\)

其中 \(f(p^k)\) 可以是由递推过来的,并不一定要求可以直接算,而且算这个的复杂度是只要不超过 \(\log n\) 即还是线性,因为质数个数可以近似为 \(\dfrac n{\log n}\) 的,多余的部分复杂度就和上文一样。

代码

ll cnt,prime[N],f[N],low[N];//low指的是这个数对应最小质因子的最高次幂对应的值
bool vis[N];
inline void GetPrimes(int d){
	vis[1]=true;f[1]=low[1]=1;//对1进行定义 
	for(ll i=2;i<=d;i++){
		if(!vis[i]) prime[++cnt]=low[i]=i,f[i]=i-1;//对质数进行定义 
		for(ll j=1;j<=cnt&&i*prime[j]<=d;j++){
			vis[i*prime[j]]=true;
			if(i%prime[j]==0){
                low[i*prime[j]]=low[i]*prime[j];
                if (low[i]==i) f[i*prime[j]]=f[i]*prime[j];//对质数的若干次幂进行定义(一般由f[i]递推);
                else f[i*prime[j]]=f[i/low[i]]*f[low[i]*prime[j]];//把当前质因子的所有项剃掉,然后把新的加回来
                break;
            }
            low[i*prime[j]]=prime[j];
			f[i*prime[j]]=f[i]*f[prime[j]];//通过 f(n),f(m) 计算 f(nm) 
		}
	}
	return ;
}

常见可以线性筛的函数

暂时鸽了。

欧拉函数

void GetPrimes(int v){
	phi[1]=vis[1]=1;
	for(register int i=2;i<=v;i++){
		if(!vis[i]) prime[++cnt]=i,phi[i]=i-1;
		for(register int j=1;j<=cnt&&i*prime[j]<=v;j++){
			vis[i*prime[j]]=true;
			if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
	return ;
}

莫比乌斯函数

void GetPrimes(int v){
	mu[1]=vis[1]=1;
	for(register int i=2;i<=v;i++){
		if(!vis[i]) prime[++cnt]=i,mu[i]=-1;
		for(register int j=1;j<=cnt&&i*prime[j]<=v;j++){
			vis[i*prime[j]]=true;
			if(i%prime[j]==0) break;
			mu[i*prime[j]]=-mu[i];
		}
	}
	return ;
}

除数函数

因数和函数

质因数个数函数

void GetPrimes(int v){
	d[1]=vis[1]=0;
	for(register int i=2;i<=v;i++){
		if(!vis[i]) prime[++cnt]=i,d[i]=1;
		for(register int j=1;j<=cnt&&i*prime[j]<=v;j++){
			vis[i*prime[j]]=true;
			if(i%prime[j]==0){d[i*prime[j]]=d[i]+1;break;}
			d[i*prime[j]]=d[i]+1;
		}
	}
	return ;
}

posted @ 2021-08-31 13:06  __Anchor  阅读(93)  评论(0)    收藏  举报