洛谷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++ 的标准输入输出流,以提高程序的性能和效率,特别是在处理大量输入输出时。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战