线性筛是一个比较有用的东东,
所以得好好记住辣。。。
对于普通的筛素数方法,
就是枚举一个i,然后和所有已知素数prime[j]相乘,
i*prime[j]就不是素数了,去掉即可。
如果这样的话是基本O(nlogn)的,
线性筛就是在这个筛法的基础上加入了一个优化,
每次让一个数只被它的最小质因子筛一次,
也就是如果i%prime[j]=0,直接break即可。
这样的话就能优化到O(n)了,代码如下:
//notprime[i]=1表示i不是质数,不然表示i为质数
//prime[i]记录第i个素数,pcnt记录素数的个数
//MAX为最大值
void Get_Prime(){
notprime[1]=1,pcnt=0;
for (int i=2;i<MAX;i++){
if (!notprime[i]) prime[++pcnt]=i;
for (int j=1;j<=pcnt;j++){
if (prime[j]*i>=MAX) break;
notprime[prime[j]*i]=1;
if (!(i%prime[j])) break; //线性筛的重要优化
}
}
}
对于欧拉函数,根据它的定义式:
phi(n)=n∗(p1−1)∗(p2−1)∗...∗(pk−1)/p1/p2/.../pk
那么对于一个n,可以在O(logn)的时间内计算phi(n),
如果要计算1~n的每个数的phi,就需要O(nlogn)的时间了;
然而其实求1~n每个数的欧拉函数是可以做到O(n)的,
就是在线性筛的基础上增加几句,,
这都基于欧拉函数的几个性质:
1. phi(i)=i−1,当i是个质数
2. phi(i∗prime[j])=phi(i)∗prime[j],当i%prime[j]=0
3. phi(i∗prime[j])=phi(i)∗(prime[j]−1),当i%prime[j]≠0
积性函数的一个东东啦。。
于是在线性筛素数的过程中我们也可以线性筛出欧拉函数了。
如果要欧拉函数求和,只要对phi做个前缀和就好辣!
//这里n是最大值
void get_eula_prime(){
notprime[1]=1,pcnt=0;
phi[1]=1;
for (int i=2;i<=n;i++){
if (!notprime[i]) phi[i]=i-1,prime[++pcnt]=i;
for (int j=1;j<=pcnt;j++){
if (i*prime[j]>n) break;
notprime[i*prime[j]]=1;
if (i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
break;
} else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
线性筛素数是一直要用的,
而线性筛欧拉函数要看情况,有时候可能直接O(logn)会更优,
要适当选择。