

不仅要看,看完算法流程还需要自己手动复述呦~
筛质数的引入:
素数的判定那一块讲的是如何判断一个数是素数,如果此时想知道 1,n 中有多少个素数,或者是 1,n 中的素数里面,第 k 个素数是多少?
一个自然的想法就是从 2 开始,依次向后对每一个数进行一次质数检验。但是这种解法相对暴力,本篇就介绍两种方法,能够快速地将 1,n 中的素数全部记录下来。
埃氏筛:
算法思想:
📌任意大于 1 的质数,它的 k(k > 1) 倍就是合数。所以我们就从小到大考虑每个数,把当前数的所有倍数记为合数,没有标记的数就是素数。
💡**代码段描述:**从 2 开始遍历到 n,遍历到当前 i 没有被标记的一定是素数,先记录、统计,再开始筛。
📌**小优化:**找到一个质数 x 的时候,我们可以从该数的 x 倍向后筛,因为小于 x 的倍数一定被之前的筛过了,例如:5 * 2,变个式,2 * 5,其实就是在找到质数 2 的时候已经筛过了。
时间复杂度:埃氏筛的时间复杂度为:O(n loglog n)。不好证,知道就行。
线性筛:
线性筛法又称欧拉筛法。
算法思想:
📌在埃氏筛的基础上做优化,上述的埃氏筛会重复标记同一个合数,线性筛就是在埃氏筛的基础上,同一个合数只被标记一次,时间复杂度将会优化到 O(n)。
💡如何做到同一个合数只会被标记一次呢?
💡让每个合数被它的最小质因数筛掉!
🤡从前往后遍历每个数 i,用 i 的质数倍去筛,同时,判断 i 是不是质数的倍数,是就停止,确保合数是被它的最小质因数筛掉的。
时间复杂度:线性筛的时间复杂度是 O(n)。
真实是 O(n + n),因为大循环 2~n 这是一个 n;筛的过程中,每个数要么被标记成合数,要么被加进 prime数组,这也是一个 n;所以每个数经历两次操作,遍历 + 筛。
OJ题来源:洛谷
OJ题名:【模板】线性筛素数
OJ题归属:数学-数论【筛质数】
解题算法:埃氏筛法 / 线性筛法
cpp#include<iostream> using namespace std; typedef long long LL; const int N = 1e8 + 10; int n, q; bool st[N]; int p[N], cnt; // 埃氏筛 //void get_prime() //{ // for (LL i = 2; i <= n; i++) // { // if (!st[i]) // { // p[++cnt] = i; // for (LL j = i * i; j <= n; j += i) // { // st[j] = true; // } // } // } //} // 线性筛 void get_prime() { for (LL i = 2; i <= n; i++) { if (!st[i]) p[++cnt] = i; // 合数被它的最小质因数筛掉 for (LL j = 1; i * p[j] <= n; j++) // j 是 p数组的下标 { st[i * p[j]] = true; // 筛 if (i % p[j] == 0) break; // 如果 i 是 质数的倍数,用这个筛的话,就满足不了合数被它的最小质因数筛掉了 } } } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> q; get_prime(); while (q--) { int k; cin >> k; cout << p[k] << endl; } return 0; }