洛谷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)发明,用于解决寻找素数的问题。

算法原理

埃氏筛的基本思想是:

  1. 初始化一个布尔类型的数组,称为标记数组(或筛选数组),用来标记每个整数是否为素数。数组的下标表示整数,数组的值为 true 表示该下标对应的整数是素数,为 false 表示不是素数。
  2. 从小到大依次遍历每个整数 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) 就可以了,因为如果 in 的因子,那么 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++ 标准输入输出流的设置,它们可以用来优化输入输出的性能。

  1. ios::sync_with_stdio(false);

    这一行代码用于取消 cincout 的同步。在默认情况下,C++ 的 cincout 是同步的,这意味着每次调用 cin 时,会先刷新 cout 的缓冲区,确保输出的内容被正确显示。但是这种同步可能会带来一些性能开销,特别是在大量输入输出操作时。

    当你调用 ios::sync_with_stdio(false); 时,表示取消 cincout 的同步,这样可以加快输入输出的速度。但是请注意,取消同步后,使用 cincout 时可能需要显式地处理缓冲区刷新的问题,以避免输出不及时或乱序输出的情况。

  2. cin.tie(nullptr);

    这行代码用于断开 cincout 的绑定。在默认情况下,cincout 是绑定在一起的,这意味着当你使用 cin 进行输入时,cout 的缓冲区会被刷新。通过调用 cin.tie(nullptr);,你可以将 cincout 的绑定解除,进一步减少不必要的性能开销。

综上所述,这两行代码的主要作用是优化 C++ 的标准输入输出流,以提高程序的性能和效率,特别是在处理大量输入输出时。

posted @ 2024-07-24 00:58  Tomorrowland_D  阅读(11)  评论(0编辑  收藏  举报