高级(线性)素数筛
在不久前,我已经介绍了简单素数筛,所以我们这次来介绍一下高级素数筛,实际上就是线性筛素数,很快地能把素数筛出来,但是我们平常竞赛的时候常用的还是那个简单素数筛,所以我这篇文章就来普及一下加自我练习一下啦!
题目思路:
之前的简单素数筛中的合数会被多个数重复标记,因此造成了冗余,所以我们要想办法在这个方面优化,我们就想到了合数只要被它的最小质因子标记了一遍之后就不再标记了,这就减少了很大一部分的冗余,速度也就变快了。
大概过程如下:
1.从2~n之间枚举
2.如果v[i]=i,那么i就是质数
3.枚举小于等于v[i]的每个质数,让v[i*p]=p,在i的基础上在乘一个质因子p,得到一个新的数。
4.因为p<=v[i],所以p就是 合数i*p的最小质因子。
| i |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
| p<=v[i] |
2 |
2,3 |
2 |
2,3,5 |
2 |
2,3,5,7 |
2 |
2,3 |
2 |
| i*p |
4 |
6,9 |
8 |
10,15,25 |
12 |
14,21,35,49 |
16 |
18,.27 |
20 |
ps:以上表格来自《算法竞赛进阶指南》
因此我们就通过这种方法制作出了线性筛素数的程序
代码如下,时间复杂度
O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int maxn=10000005; int v[maxn],p[maxn]; void primes(int n) { int m=0; memset(v,0,sizeof(v)); for(int i=2;i<=n;i++) { if(v[i]==0) { v[i]=i; p[++m]=i; } for(int j=1;j<=m;j++) { if(p[j]>v[i]||p[j]>(n/i)) break; v[i*p[j]]=p[j]; } } for(int i=1;i<=m;i++) { cout<<p[i]<<endl; } } int main() { int n; cin>>n; primes(n); return 0; }
|
接下来我们来一道例题练练手
题目:luogu P3383 【模板】线性筛素数
我帮大家复制了一下题面
题目描述
如题,给定一个范围N,你需要处理M个某数字是否为质数的询问(每个数字均在范围1-N内)
输入格式
第一行包含两个正整数N、M,分别表示查询的范围和查询的个数。
接下来M行每行包含一个不小于1且不大于N的整数,即询问该数是否为质数。
输出格式
输出包含M行,每行为Yes或No,即依次为每一个询问的结果。
输入输出样例
输入 #1
输出 #1
说明/提示
时空限制:500ms 128M
数据规模:
对于30%的数据:N<=10000,M<=10000
对于100%的数据:N<=10000000,M<=100000
样例说明:
N=100,说明接下来的询问数均不大于100且不小于1。
所以2、3、97为质数,4、91非质数。
故依次输出Yes、Yes、No、No、Yes。
题目思路:这道题很简单,就是把刚才的代码稍加改动,判断一下只要v[i]==i,就是素数,否则就不是素数
代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int maxn=10000005; int v[maxn],p[maxn]; void primes(int n) { int m=0; memset(v,0,sizeof(v)); for(int i=2;i<=n;i++) { if(v[i]==0) { v[i]=i; p[++m]=i; } for(int j=1;j<=m;j++) { if(p[j]>v[i]||p[j]>(n/i)) break; v[i*p[j]]=p[j]; } } } int main() { int n,q,r; cin>>n>>q; primes(n); for(int i=1;i<=q;i++) { cin>>r; if(r==0||r==1) { cout<<"No"<<endl; continue; } if(v[r]==r) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; }
|
下集预告:单源最短路径优化版或其他