欧拉筛法
普通的筛法,即Eratosthenes筛法,复杂度为
O(nloglogn)
,当范围大时,就扛不住了,需要利用欧拉筛法,也称线性筛法,下面还会介绍利用欧拉筛法的优美性质来求积性函数。
先给出欧拉筛法的代码:
for(int i=2;i<=n;i++){
if(vis[i])prime[++prime[0]]=i;
for(int j=1;j<=prime[0]&&prime[j]*i<=n;j++){
vis[i*prime[j]]=0;
if(!(i%prime[j]))break;
}
}
欧拉筛法的思想是:
1.如果这个数是质数,那么就将它与之前的质数的乘积挖掉
2.如果这个数是合数,那么就将它与从2到它最小的质因子之间的质数的乘积分别挖掉
考虑一个数
n=p1k1∗p2k2∗......
,对于大于p1的质数j,令
k′=n/p1
,
n∗j=p∗k′∗j
,所以这样的数可以通过p1来筛到,没有必要再去枚举。并且,这样的话,n只有可能会被
p1k1−1∗p2k2∗......
筛到,所以每个数只会被筛一次。所以,它的复杂度为
O(n)
用欧拉筛法求欧拉函数
先来回顾一下欧拉函数有什么性质:
1.它是一个积性函数,即当(i,j)=1,
ϕ(i∗j)=ϕ(i)∗ϕ(j)
2.对于任意的质数p,
ϕ(p)=p−1
3.对于任意的质数p,
ϕ(pk)=(p−1)∗pk−1
所以我们分两种情况讨论:
1.
n%p>0
显然,
ϕ(n∗p)=ϕ(n)∗(p−1)
2.
n%p=0
由于n,p不互质,所以不能直接用积性推了。假设n中有k个p这个质因子,
ϕ(n∗p)=ϕ(n/pk)∗ϕ(pk+1)=ϕ(n/pk)∗pk∗(p−1)=ϕ(n/pk)∗ϕ(pk)∗p=ϕ(n)∗p
代码如下:
for(int i=2;i<=n;i++){
if(vis[i])prime[++prime[0]]=i,phi[i]=i-1;
for(int j=1;j<=prime[0]&&prime[j]*i<=n;j++){
vis[i*prime[j]]=0;
if(!(i%prime[j])){
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}