【模板】線性篩素數
題目背景
本題已更新,從判斷素數改為了查詢第 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++ 的標準輸入輸出流,以提高程式的效能和效率,特別是在處理大量輸入輸出時。