线性筛法及积性函数总结(欧拉函数、莫比乌斯函数、约数和函数、约数个数函数)
线性筛法在数论中起着至关重要的作用,对于一部分求解有关积性函数的问题可以大大降低时间复杂度。线性筛法中,除了线性筛质数,所要筛的函数必须是积性函数,而线性筛这些函数的基础也是线性筛质数。先来解释一下什么是积性函数?积性函数就是指对于一个函数f,f(1)=1且对于任意两个互质的数x,y满足f(x)*f(y)=f(x*y)。而如果任意两个数x,y都满足以上等式,那么这个函数就是完全积性函数。
常见且实用的积性函数有:
1、l(x)=1,也叫单位函数,卷积单位元
2、id(x)=x
3、Φ(x),欧拉函数,1~x中与x互质的数的个数
4、μ(x),莫比乌斯函数,当x=1时,函数值为1;当x为k个质数的一次幂乘积时,函数值为(-1)k;其他情况为0
下面进入正题:
为了方便描述,设f[]为对应积性函数,p[]为存质数的数组,p[j]是i的最小质因子,prime[j]与p[j]同义
一、欧拉函数
1、性质
- 若p为质数,则Φ(p)=p-1
- 若x与y互质,则Φ(x*y)=Φ(x)*Φ(y)
- 若n为奇数,则Φ(2n)=Φ(n),(其实就是上一个性质的特殊情况)
- 若n为质数p的k次幂,则Φ(n)=(p-1)*pk-1,(除p的倍数外都与n互质)
- Σd|nΦ(d)=n
2、线性筛
当i为质数时,f[i]=i-1
当i%p[j]!=0时,i与p[j]互质,f[i*p[j]]=f[i]*(p[j]-1)
当i%p[j]==0时,设ak为i的一个质因子,Φ(i)=i*Π((ak-1)/ak),因此f[i*p[j]]=p[j]*f[i]
#include<set> #include<map> #include<queue> #include<cmath> #include<stack> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; bool vis[10000010]; int n; int f[10000010]; int prime[1000000]; int cnt; void find() { f[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) { prime[++cnt]=i; f[i]=i-1; } for(int j=1;j<=cnt&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { f[i*prime[j]]=prime[j]*f[i]; break; } else { f[i*prime[j]]=(prime[j]-1)*f[i]; } } } } int main() { scanf("%d",&n); find(); }
二、莫比乌斯函数
1、性质
- Σd|nμ(d)=ε(n),当n=1时,ε(n)=1;当n≠1时,ε(n)=0,(莫比乌斯反演用到的重要性质)
2、线性筛
当i为质数时,f[i]=-1
当i%p[j]!=0时,说明i中没有p[j],那么f[i*p[j]]=-f[i]
当i%p[j]==0时,f[i*p[j]]=0
#include<set> #include<map> #include<queue> #include<cmath> #include<stack> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; bool vis[10000010]; int n; int f[10000010]; int prime[1000000]; int cnt; void find() { f[1]=1 for(int i=2;i<=n;i++) { if(!vis[i]) { prime[++cnt]=i; f[i]=-1; } for(int j=1;j<=cnt&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { f[i*prime[j]]=0; break; } else { f[i*prime[j]]=-f[i]; } } } } int main() { scanf("%d",&n); find(); }
三、约数个数函数
1、性质
对于任意一个大于1的正整数n都能质因数分解为n=p1a1*p2a2*……*pkak,f(n)=(1+a1)*(1+a2)*……*(1+ak)
2、线性筛
因为每个数由它最小的质因子筛出,因此要开一个辅助数组d[i]表示i最小质因子的次幂
当i为质数时,f[i]=2,d[i]=1
当i%p[j]!=0时,由于是积性函数,f[i*prime[j]]=f[i]*f[prime[j]]=2*f[i],d[i]=1
当i%p[j]==0时,根据性质可知f[i*prime[j]]=f[i]/(d[i]+1)*(d[i]+2),d[i*prime[j]]=d[i]+1
#include<set> #include<map> #include<queue> #include<cmath> #include<stack> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; bool vis[10000000]; int n; int f[10000000]; int prime[10000000]; int d[10000000]; int cnt; void find() { f[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) { prime[++cnt]=i; f[i]=2; d[i]=1; } for(int j=1;j<=cnt&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { f[i*prime[j]]=f[i]/(d[i]+1)*(d[i]+2); d[i*prime[j]]=d[i]+1; break; } else { f[i*prime[j]]=2*f[i]; d[i*prime[j]]=1; } } } } int main() { scanf("%d",&n); find(); }
四、约数和函数
1、性质
对于任意大于1的正整数质因数分解,n=p1a1*p2a2*……*pkak
f(n)=(1+p11+p12+……+p1a1)*(1+p21+p22+……+p2a2)*……*(1+pk1+pk2+……+pkak)
2、线性筛
设d[i]表示i最小质因子的各次幂之和
当i为质数时,f[i]=i+1,d[i]=i+1
当i%p[j]!=0时,f[i*p[j]]=f[i]*f[p[j]],d[i*p[j]]=1+p[j],积性函数性质
当i%p[j]==0时,d[i*p[j]]=d[i]*p[j]+1,f[i*p[j]]=f[i]/d[i]*d[i*p[j]]
#include<set> #include<map> #include<queue> #include<cmath> #include<stack> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define ll long long using namespace std; bool vis[10000000]; int n; ll f[10000000]; int prime[10000000]; ll d[10000000]; int cnt; void find() { f[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) { prime[++cnt]=i; f[i]=i+1; d[i]=i+1; } for(int j=1;j<=cnt&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { d[i*prime[j]]=d[i]*prime[j]+1; f[i*prime[j]]=f[i]/d[i]*d[i*prime[j]]; break; } else { f[i*prime[j]]=f[i]*f[prime[j]]; d[i*prime[j]]=1+prime[j]; } } } } int main() { scanf("%d",&n); find(); }
总结:
线性筛积性函数的方法一般都相同,都是利用积性函数互质可相乘的性质来筛。
具体说一下方法:
- 首先需要一个数组g[i]存i最小质因子的最高次幂的乘方,所要筛的积性函数还是用f[i]来表示,p[j]表示i的最小质因子。
- 当i为质数时,直接求就好了。
- 当i%p[j]!=0时,由积性函数性质直接相乘->f[i*prime[j]]=f[i]*f[prime[j]],d[i*prime[j]]=prime[j]
- 当i%p[j]==0时,这时设t=i/d[i],t就与p[j]互质了,i*prime[j]=t*prime[j]*d[i],f[i*prime[j]]=f[t]*f[prime[j]*d[i]],d[i*prime[j]]=d[i]*prime[j]
- 但当i=pk形式的数时,这么做是不行的,我们就只能暴力求了。