标签:+= 模版题 暴力 线性筛 print main 正整数 题目 c代码
如题,给定一个范围\(n\),有\(q\)个询问,每次输出第\(k\)小的素数。
第一行包含两个正整数 \(n,q\),分别表示查询的范围和查询的个数。
接下来\(q\)行每行一个正整数\(k\),表示查询第\(k\)小的素数。
输出\(q\)行,每行一个正整数表示答案。
对于 \(100\%\) 的数据,\(n = 10^8,1≤q≤10^6\),保证查询的素数不大于\(n\)。
这题是一道筛法模版题,第一个先想到的应该是暴力枚举,但是暴力枚举时间复杂度显然太高,然后经过一番搜寻,找到了埃式筛。
这个筛法的时间复杂度为\(O(n\log\log n)\),算的上是比较优秀了。
如果\(x\)是合数,那么\(x\)的倍数也一定是合数。
这个规则很容易就知道了是对的,是吧,这样我们要求100以内的数就只需将1到10以内的倍数枚举并标记,没有标记的数就是质数,这个筛法就是这个思想。
埃式筛的代码如下:
int Eratosthenes(int n) {
int p = 0;
for (int i = 0; i <= n; ++i) is_prime[i] = 1;
is_prime[0] = is_prime[1] = 0;
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
prime[p++] = i; // prime[p]是i,后置自增运算代表当前素数数量
if ((long long)i * i <= n)
for (int j = i * i; j <= n; j += i)
// 因为从 2 到 i - 1 的倍数我们之前筛过了,这里直接从 i
// 的倍数开始,提高了运行速度
is_prime[j] = 0; // 是i的倍数的均不是素数
}
}
return p;
}
但是这个还是有点慢,这个数据可能会过不了,所以我们又找到了另一个更快的算法:欧拉筛(线性筛)。
埃氏筛法仍有优化空间,它会将一个合数重复多次标记,所以线性筛就是来解决这个事情的。
void init() {
for (int i = 2; i < MAXN; ++i) {
if (!vis[i]) {
prime[cnt++] = i;
}
for (int j = 0; j < cnt; ++j) {
if (1ll * i * prime[j] >= MAXN) break;
vis[i * prime[j]] = 1;
if (i % prime[j] == 0) {
// 换言之,i 之前被 pri[j] 筛过了
// 由于 pri 里面质数是从小到大的,所以 i 乘上其他的质数的结果一定也是
// pri[j] 的倍数 它们都被筛过了,就不需要再筛了,所以这里直接 break
// 掉就好了
break;
}
}
}
}
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
bool visited[100000010];
int prime[6000010];
void shai(int n){
int cnt = 0;
for(int i = 2;i<=n;i++)
{
if(!visited[i]) prime[cnt++] = i;
for(int j=0;j<cnt;j++){
if(i * prime[j] > n)
break;
visited[i * prime[j]] = 1;
if(i % prime[j] == 0)
break;
}
}
}
int main()
{
int n,m,x;
cin>>n>>m;
shai(n);
for(int i=0;i<m;i++)
{
scanf("%d",&x);
printf("%d\n",prime[x-1]);
}
getchar();getchar();
return 0;
}
标签:+= 模版题 暴力 线性筛 print main 正整数 题目 c代码
原文地址:https://www.cnblogs.com/kasugano2/p/14629228.html