欧拉筛法模板and 洛谷 P3383 【模板】线性筛素数(包括清北的一些方法)
题目描述
如题,给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内)
输入格式
第一行包含两个正整数N、M,分别表示查询的范围和查询的个数。
接下来M行每行包含一个不小于1且不大于N的整数,即询问该数是否为质数。
输出格式
输出包含M行,每行为Yes或No,即依次为每一个询问的结果。
当然这是一道很裸的板子题,但是却牵扯到了一个非常有用的东西:
素数筛法
首先,我们知道素数筛法主要就是以下几种
第一:
无脑筛
其实就是从2到n遍历一遍,没什么可讲的,顶多把n优化成sqrt(N),所是这种算法的时间复杂度就是O(sqrt(N))不可否定的是,这是已知判断单一数的最快而且是最精准的方法了,绝对不会出错
第二:
Miller-rabin素性测试
如果n为素数,取a<n,设n-1=d*2r,则要么ad≡1(mod n)要么存在0<=i<r,使得ad*2^t≡-1(mod n),要么存在0<=i<r,使得ad*2^t≡-1(mod n)(有可能都满足)
对于任意一个a,如果满足这两个条件,n有可能是质数,但a如果不满足这两个条件中的任何一个,它一定不是质数。找k个a,如果都满足这两个条件,k-1个“更”有可能是质数
在选取k的时候,最好选取2,3,5,7,13,29,37,89至少保证int范围内不会出错
因为筛法的不确定性来自于随机的a,但是当选取的数足够好,就没有问题;
如果n是素数,取a<n,舍n-1=d*2r,则要么ad≡1(mod n),要么存在0<=i<r,使得a
部分代码:
int gg[8]={2,3,5,7,13,29,37,89}; long long kuaisumi(long long a,long long b1,long long c) { long long i=a; while(b1) { if(b1&1) { s=(s*i)%c; } i=(i*i)%c; b1>>=1; } return s%c; } bool miller_rabin(int a,int n) { int d=n-1,r=0; while(d%2==0) d/=2,r++; int x=kuaisumi(a,d,n); if(x==1)return true; for(int i=0;i<r;i++) { if(x==n-1)return true ; x=(long long )x*x%n; } return false;//可以对照素性测试看 } bool is_prime (int n) { if(n<=1)return false ; for(int a=0;a<8;a++) if(n==gg[a])return true;//一个个试 for(int a=0;a<8;a++) if(!miller_rabin(gg[a],n))return false; return true; }
现在我们讲一下筛某一个范围的数的方法,
第三:
埃拉托色尼筛法(埃氏筛)
主要思想就是把所有质数的整倍数筛掉,速度比较快,也不难想,时间复杂度o(nloglogn)
代码实现:
#include<stdio.h> #include<math.h> int main() { int i,j,k; int a[101]; for(i=1;i<=100;i++) a[i]=i; a[1]=0; //先挖掉a[1] for(i=2;i<sqrt(100);i++){ for(j=i+1;j<=100;j++){ if(a[i]!=0&&a[j]!=0){ if(a[j]%a[i]==0){ a[j]=0; //把非素数挖掉,不是素数的都赋值为0 } } } } printf("\n"); for(i=1,k=0;i<=100;i++){ if(a[i]!=0){ //选出值不为0的数 即素数 //cout<<" "<<a[i]; printf("%d ",a[i]); k++; } if(k==10){ printf("\n"); k=0; } } printf("\n"); return 0; }
还有一个是对埃氏筛的优化,叫做欧拉筛,也叫作线性筛
代码实现:
#include<cstdio> #include<iostream> using namespace std; int tot,n,b[9999999],pri[9999999];//b[i]存的是i是否为质数,0为质数,1为合数;pri[i]存的是第i个质数 inline void shai() { //b[1]=1; for(int i=2;i<=n;i++)//从2到n,因为1不是质数可以跳过,当然有的时候可能会用到b[1],这个时候需要赋特值如上 { if(!b[i])pri[++tot]=i; for(int j=1;j<=tot&&i*pri[j]<=n;++j) { b[i*pri[j]]=1; if(i%pri[j]==0)break;//如果i%pri[j]==0,就说明是i的倍数的数一定是某质数的倍数,这个时候就可以把它去掉从而节省时间 } } } int main() { cin>>n; shai(); for(int i=1;i<=tot;i++) { cout<<pri[i]<<endl;//这里是输出n以内的质数,如果想判断一个数是否为质数可以看b[i] } }
所有的筛法就讲完了,最后我们贴一下AC代码:
#include<cstdio> #include<iostream> #include<cmath> using namespace std; int tot,n,m,b[9999999],pri[9999999],a[9999999]; inline void shai() { for(int i=2;i<=n;i++) { if(!b[i])pri[++tot]=i; for(int j=1;j<=tot&&i*pri[j]<=n;++j) { b[i*pri[j]]=1; if(i%pri[j]==0)break; } } } int main() { cin>>n>>m; shai(); for(int i=1;i<=m;i++) { cin>>a[i]; } for(int i=1;i<=m;i++) { if(a[i]==1) { cout<<"No"<<endl; } else { if(b[a[i]]==0) cout<<"Yes"<<endl; else cout<<"No"<<endl; } } return 0; }