洛谷 P3383 線性篩素數

2021-08-10 15:42:34 字數 3073 閱讀 1289

洛谷 p3383

題目描述:

如題,給定乙個範圍n,你需要處理m個某數字是否為質數的詢問(每個數字均在範圍1-n內)

資料範圍:

對於100%的資料:n<=10000000,m<=100000

樣例輸入:

100 5

2 3

4 91

97樣例輸出:

yes

yes

no no

yes

#include

#define max 100000000

using

namespace

std;

bool ifnot_prime[max+1]; //0---素數 1---不是素數

int prime[max],pnum;

void init(int n) //核心步驟,篩表操作

}}int main()

}

這麼大資料範圍不可能乙個個判斷【誤……記得有個什麼東西用費馬小定理很快就能得出乙個數是不是質數……原來學過的都忘完了orz……【【

【↑: 題解裡有個什麼叫miller_rabin的演算法就是這個思路可以看看……

因此我們想到篩選操作

引出一件小學做過的事:

小學時做過乙個100內的素數表

步驟是:

1. 把所有2的倍數[4.6.8]【除了本身】劃上一條槓,標記為不是素數

2. 把3所有的倍數[6.9.12]【除了本身】也如上照做

3. 然後依次5、7、11等等之內所有的素數都如上照做

則最後得出的表,沒有劃槓的便是100以內所有的素數表

以上便是篩選表的思路。

基本的操作大家肯定都會了吧(:зゝ∠)……這裡只是來講下優化

我們發現上述操作中 2 篩除了 4、6、8、10、……

3 篩除了 6、9、12、……

發現 6 分別被2、3都篩過了

如果能讓 6 只被篩一次,則效率能極大優化

因此先不管這個優化,先講第乙個優化……

首先,我們把原來的思路:

把乙個素數從頭到尾篩完,再篩下乙個素數

e.g.

先來 2 篩除 4、6、8、10、12、14、……

過後 3 篩除 6、9、12、15、……

改為—>

只篩除所找到素數的某倍數那個數,

這個倍數k從2迴圈到所要找區間的一半【如要打100000內素數表,k=2—>50000 [這是個小優化,因為原來我是直接2—>100000,發現最小素數2*這麼大早就超界了,因此只用到/2這麼多就行了]

而且 所找到的素數,是邊篩邊找的,而這個邊篩邊找恰好可以用 用來記倍數的k 來表示

【知道我的語言是聽不懂的所以來看栗子吧……

e.g.

倍數k=2 2為素數,入素數隊, 2 篩除 2*2—>4;

k=3 3為素數,入素數隊, 2 篩除 2*3—>6; 3 篩除 3*3—>9;

k=4 4不為素數,不管, 2 篩除 2*4—>8; 3 篩除 3*4—>12;【然而這裡還是會重複篩選,當k=6時 2 會篩除 12;

光是這樣做,也就是 init中 沒有 if (top % prime[c] == 0) break; 這步時

void init(int n)                //核心步驟,篩表操作 

}}

就已經得到了極大優化

n=10000000,用時:.374,甚至已經可以ac了orz……【

接下來便是防止重複篩選的關鍵步驟!

當倍數k不斷增長的時候,2總會把所有 2所有的倍數 給篩選完

因此 3 本應可以篩 12,但是我們為何不讓 2 把他篩了呢?

觀察一下素數隊,肯定是單調遞增的,也就是存的為 2、3、5、7、11 這樣的質數

因此這樣就更好操作了!因為每次是先讓2乘倍數k,再讓3乘再5……

因此我們讓這個倍數k,對所翻倍的素數 prime[c] 取模一下

emmmm聽不懂不要急來強行舉例解釋一波……

當top=4的時候【最早開始產生重複篩數的top 3*4=12 本應被 2 篩除

1st. 2 篩除 2*2 —> 4; 發現 4%2==0, 那麼這個就代表著,4是2的倍數,也就是4可以寫成2*k(k∈z)(當然k=2)這種形式,

因此之後篩選的時候

(本來不該做這步的) 2nd. 3 應該篩除 3*4—> 12 的, 然而 由上面的 可知 4 = 2*k 因此 3*4 就可以為—> 3*2*k

換個順序 3*4 —> 2*3*k

這代表著什麼?

代表著 12 這個數 是可以被 2 在倍數為3*k的時候 篩除的!

因此就不用繼續篩下去了,坐等今後2把這個數給篩了

e.g. 當top=9的時候,素數隊prime=

1st. 2 篩除 18, 9%2!=0, 證明9不能寫為 2*k (k∈z)這種形式的,因此繼續

2nd. 3 篩除 27, 9%3==0, 證明9能被寫為 3*k (k∈z)這種形式的

因此如果繼續的話 5*9 —> 3*k*5 ,即3之後可以把45這個數篩除

7*9 —> 3*k*7 ,即3之後可以把63這個數篩除

所以這裡,明智的break,讓今後3去篩他

以上 換到程式裡來

簡單的加一句 if (top % prime[c] == 0) break;

void init(int n)                //核心步驟,篩表操作 

}}

n=10000000,用時:.124,強無敵!……

【那個n/2+1改為n,用時也才.156,所以這個是後來才發現的小優化啦(:зゝ∠)……

以上 */

洛谷P3383線性篩素數

中體現在那個 break prime 陣列 中的素數是遞增的,當 i 能整除 prime j 那麼 iprime j 1 這個合數肯定被 prime j 乘以某個數篩掉。因為i中含有 prime j prime j 比 prime j 1 小。接下去的素數同理。所以不用篩下去了。在滿足 i prme...

洛谷 P3383 模板 線性篩素數

題目大意 如題,給定乙個範圍n,你需要處理m個某數字是否為質數的詢問 每個數字均在範圍1 n內 時空限制 500ms 128m 資料規模 對於30 的資料 n 10000,m 10000 對於100 的資料 n 10000000,m 100000 題解 數學方法 線性篩素數 很多人都是找到乙個素數,...

洛谷 P3383 模板 線性篩素數

如題,給定乙個範圍n,你需要處理m個某數字是否為質數的詢問 每個數字均在範圍1 n內 輸入格式 第一行包含兩個正整數n m,分別表示查詢的範圍和查詢的個數。接下來m行每行包含乙個不小於1且不大於n的整數,即詢問該數是否為質數。輸出格式 輸出包含m行,每行為yes或no,即依次為每乙個詢問的結果。1 ...