洛谷P3383 【模板】线性筛素数
【模板】线性筛素数
题目背景
本题已更新,从判断素数改为了查询第 k 小的素数
提示:如果你使用 cin
来读入,建议使用 std::ios::sync_with_stdio(0)
来加速。
题目描述
如题,给定一个范围 n,有 q 个询问,每次输出第 k 小的素数。
输入格式
第一行包含两个正整数 n,q,分别表示查询的范围和查询的个数。
接下来 q 行每行一个正整数 k,表示查询第 k 小的素数。
输出格式
输出 q 行,每行一个正整数表示答案。
样例 #1
样例输入 #1
100 5
1
2
3
4
5
样例输出 #1
2
3
5
7
11
提示
【数据范围】
对于 100% 的数据,n = 108,1<=q<=106,保证查询的素数不大于 n。
Data by NaCly_Fish.
埃氏筛法求质数
埃氏筛(Sieve of Eratosthenes)是一种用来找出一定范围内所有素数的经典算法。它由古希腊数学家埃拉托斯特尼斯(Eratosthenes)发明,用于解决寻找素数的问题。
算法原理
埃氏筛的基本思想是:
- 初始化一个布尔类型的数组,称为标记数组(或筛选数组),用来标记每个整数是否为素数。数组的下标表示整数,数组的值为
true
表示该下标对应的整数是素数,为false
表示不是素数。 - 从小到大依次遍历每个整数
i
,若发现i
是素数,则将i
的所有倍数标记为非素数(即将对应位置的布尔值设为false
),除了i
本身。
具体步骤如下:
- 初始化一个布尔数组
is_prime
,将数组中所有元素初始化为true
。 - 将
is_prime[0]
和is_prime[1]
设为false
,因为 0 和 1 不是素数。 - 从
2
开始遍历到sqrt(n)
,对于每个素数i
,将i
的所有倍数(除了i
本身)设为false
。 - 最终,所有值为
true
的下标即为素数。
算法优化
- 减少遍历范围: 在埃氏筛中,我们只需要遍历到
sqrt(n)
就可以了,因为如果i
是n
的因子,那么n/i
也一定是n
的因子。 - 空间优化: 如果要找出的素数范围不是很大,可以优化空间。例如,使用标记数组只标记奇数,可以将空间使用减半。
示例
vector<int> sieve(int n) {
vector<bool> is_prime(n + 1, true);
vector<int> primes;
//这里i不能只遍历到sqrt(n)就结束
//这里的条件 i <= n / i 可能会导致问题。当 i 很大时,n / i 可能会得到一个非常小的数,比如当 i 接近 sqrt(n) 时,
// n / i 可能会变成0或者1,这样循环就会提前结束,导致部分素数没有被正确标记。
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
primes.push_back(i);
for (long long j = (long long)i * i; j <= n; j += i) {
is_prime[j] = false;
}
}
}
return primes;
}
有了这个模板以后,这道题我们直接套模板就可以解决了:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> sieve(int n) {
vector<bool> is_prime(n + 1, true);
vector<int> primes;
//这里i不能只遍历到sqrt(n)就结束
//这里的条件 i <= n / i 可能会导致问题。当 i 很大时,n / i 可能会得到一个非常小的数,比如当 i 接近 sqrt(n) 时,
// n / i 可能会变成0或者1,这样循环就会提前结束,导致部分素数没有被正确标记。
for (int i = 2; i <= n; ++i) {
if (is_prime[i]) {
primes.push_back(i);
for (long long j = (long long)i * i; j <= n; j += i) {
is_prime[j] = false;
}
}
}
return primes;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
vector<int> primes = sieve(n);
while (q--) {
int k;
cin >> k;
// 输出第 k 小的素数
cout << primes[k - 1] << "\n";
}
return 0;
}
代码解释:
ios::sync_with_stdio(0);
cin.tie(nullptr);
这两行代码是关于 C++ 标准输入输出流的设置,它们可以用来优化输入输出的性能。
-
ios::sync_with_stdio(false);
这一行代码用于取消
cin
和cout
的同步。在默认情况下,C++ 的cin
和cout
是同步的,这意味着每次调用cin
时,会先刷新cout
的缓冲区,确保输出的内容被正确显示。但是这种同步可能会带来一些性能开销,特别是在大量输入输出操作时。当你调用
ios::sync_with_stdio(false);
时,表示取消cin
和cout
的同步,这样可以加快输入输出的速度。但是请注意,取消同步后,使用cin
和cout
时可能需要显式地处理缓冲区刷新的问题,以避免输出不及时或乱序输出的情况。 -
cin.tie(nullptr);
这行代码用于断开
cin
和cout
的绑定。在默认情况下,cin
和cout
是绑定在一起的,这意味着当你使用cin
进行输入时,cout
的缓冲区会被刷新。通过调用cin.tie(nullptr);
,你可以将cin
和cout
的绑定解除,进一步减少不必要的性能开销。
综上所述,这两行代码的主要作用是优化 C++ 的标准输入输出流,以提高程序的性能和效率,特别是在处理大量输入输出时。