埃氏筛 O(nloglogn)
埃氏筛的意义很简单,每次我对现在遍历到的素数进行枚举,对它的所有的在区间内的倍数进行枚举,这些枚举的数字都是合数,当我们把所有的区间内素数的倍数枚举完时,把他们都做上标记,未被标记的就是素数.这样还可以保证下次枚举的必定是素数,因为合数已经被标记了.
void getp()
{
int cnt=0;
vis[1]=1;
for(int i=2;i<=n;i++)//枚举
if(vis[i]==0)//判断是否为素数
for(int j=2;j*i<=n;j++)//枚举倍数
vis[i*j]=1;
return ;
}
/*
vis标记为1的就不是素数,注意埃氏筛拓展之后就是区间筛
*/
但是埃氏筛有个缺点,我们每次进行素数倍数的枚举,会产生的重复的枚举,比如一个数字是某几个素数的倍数,就会重复枚举几次.如果我们能够消除重复的枚举,在这个前提之下,就创造出了欧拉筛.
欧拉筛 O(N)
欧拉筛的含义就是优化的埃氏筛.每次我们判断某个数是否为素数,再去枚举,只不过它的欧典在于每个数只会去枚举一次.当当前的数和遍历的质因数整除时,就需要注意了,这个数已经枚举过了,直接break即可.
void getp()
{
int cnt=0;
for(int i=2;i<=n;i++)
{
if(vis[i]==0)
p[++cnt]=i;
for(int j=1;j<=cnt&&p[j]*i<=n;j++)
{
vis[i*p[j]]=1;
if(i%p[j]==0)
break;
}
}
return ;
}
区间筛 时间复杂度类似于埃氏筛
区间筛是一种变种的埃氏筛,是对于某个数值大,但是区间长度仍在计算范围内的区间进行区间内素数问题求解的算法.我们首先要枚举2到的质数.然后用这些质数去枚举在区间[L,R]间的倍数,这些枚举的倍数就是合数,直接打上标记,区间内未被标记的就是素数.
void segment_sieve(int a,int b)
{
for(int i=0;i<1e6+5;i++)
isprime[i]=lowisprime[i]=true;
for(int i=2;i*i<b;i++)
{
if(lowisprime[i])
{
for(int j=2*i;j*j<b;j+=i)
lowisprime[j]=false;
for(long long j=(a/i)*i;j<b;j+=i)
if(j>=a&&j<b)
isprime[j-a]=false;
}
}
}