线性筛素数和理解 洛谷P3383

时间:2021-09-13 21:04:20

题目链接:https://www.luogu.org/problemnew/show/P3383

线性筛法筛素数的特点是每一个数字只被遍历一次,即时间复杂度为O(n),所以说他是线性的,并且所有的非素数都是被它的最小素因子筛去的。

我们先看板子:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque>
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 10000005
int prime[maxn];//prime数组存我们筛出来的素数
bool vis[maxn];//在这里面vis[i]辨别i是不是一个素数,假如vis[i]==false,那么i是素数,否则不是
int n,m,k,t,cnt;
void play_table(){
cnt=;//一个计数器,记录素数的个数
memset(vis,false,sizeof(vis));
vis[]=vis[]=true;//0和1提前赋值为true
for(int i=;i<maxn;i++){
if(vis[i]==false){
prime[cnt++]=i;//记录素数
}
for(int j=;j<cnt&&prime[j]*i<maxn;j++){
vis[prime[j]*i]=true;
if(i%prime[j]==)
break;
}
}
}
int main()
{
play_table();
while(scanf("%d%d",&n,&m)!=EOF){
for(int i=;i<m;i++){
scanf("%d",&k);
if(vis[k]==false)
printf("Yes\n");
else
printf("No\n");
}
}
return ;
}

我们看里面关键的一部分:

        for(int j=;j<cnt&&prime[j]*i<maxn;j++){
vis[prime[j]*i]=true;
if(i%prime[j]==)
break;
}

假设现在我们有一个i,我们执行上面部分的代码:

for循环里面除了j<cnt外还要加上&&prime[j]*i<maxn,防止数组越界。

我们知道2它是最小的素数,那么2*(任何数字)得出的都是一个非素数,并且这个非素数一定是被它的最小素因子2给筛掉的,这里面的prime[0]一定是2,所以我们先执行vis[prime[j]*i]=true,先把2*i标记为非素数。

接下来一句

            if(i%prime[j]==)
break;

假如 i 被prime[j]整除,那么我们就跳出循环。

首先假如 i 被prime[j]整除,那么 i 一定有一个素因子是prime[j],i 一定是一个合数;

因为我们筛出来的素数是从小到大的,所以prime[j+1]>prime[j]是成立的,假如我们现在不跳出,那么我们接下来肯定会有vis[i*prime[j+1]]=true,这样的话,prime[j+1]*i这个非素数就是被他的素因子prime[j+1]筛去,但是事实上 i 这个数字是一个合数,它是可以分解的,假设i=prime[j]*d,在后面肯定还会有i=prime[j+1]*d,vis[i*prime[j]]=true 再把这个非素数筛一次,这样我们就不符合非素数被它的最小素因子筛去并且每一个数字只遍历一次的原则了。

所以我们在这里面要跳出,等i==prine[j+1]*d时,我们这个时候vis[i*prime[j]]=true,用prime[j]这个最小素因子把prime[j]*prime[j+1]*d这个非素数筛去。