众所周知,即使是经过优化的埃氏筛,其复杂度依然为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;
}