【模板】【线性筛】
【模板】【线性筛】
众所周知,即使是经过优化的埃氏筛,其复杂度依然为O(N log log N)
但我们需要一个O(N)的算法————线性筛
我们发现之所以埃氏筛会重复标记合数,是因为其没有确定该合数的唯一产生方式
线性筛的对策是————只用该合数的最小质因子标记该合数
于是,我们用一个v数组记录2~N中每个数的最小质因子
算法流程
- 枚举i从2~N;
- 若v[i]==0,即没有被标记过,说明i为质数,将i加入质数集合,并标记v[i]=i;
- 扫描不大于v[i]的每个质数p,令v[i*p]=p。
正确性
为什么所有合数在这个过程中都会被标记?因为任意一个合数i都一定能用它的最小质因子p0与一个小于i的整数i/p0的积表示
额……然后就证完了?(毕竟确实很显然)
Code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
register int x=0,w=1;
register char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-'){ch=getchar();w=-1; }
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar(); }
return x*w;
}
const int M=1e8+10;
int n,q,k,cnt;
int v[M],prime[M/8+100];
int main()
{
n=read();
q=read();
for(int i=2;i<=n;++i)
{
if(v[i]==0){
prime[++cnt]=i;
v[i]=i;
}
for(int j=1;j<=cnt;++j)
{
if(prime[j]>v[i]||prime[j]*i>n) break;
v[prime[j]*i]=prime[j];
}
}
for(int i=1;i<=q;++i){
k=read();
printf("%d\n",prime[k]);
}
return 0;
}
还有一种方法可以实现线性筛:v数组可以不去存一个数的最小质因子,而只是表示该数是否为素数。
代码如下:
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
register int x=0,w=1;
register char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-'){ch=getchar();w=-1;}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
inline void write(int x)
{
if(x<0){putchar('-');x=~(x-1);}
if(x>9) write(x/10);
putchar(x%10+'0');
}
int n,m;
vector<int>p;
int v[100000100];
int main()
{
n=read();m=read();
for(int i=2;i<=n;++i)
{
if(v[i]==0) p.push_back(i);
for(int j=0;p[j]*i<=n;++j)
{
v[p[j]*i]=1;
if(i%p[j]==0) break;//一旦p[j]为i的最小质因子,跳出循环
}
}
for(int i=1;i<=m;++i)
{
write(p[read()-1]);puts("");
}
return 0;
}

浙公网安备 33010602011771号