計蒜客 2017 NOIP 提高組模擬賽(四)Day1 T1 小X的質數 線性篩素數
小 X 是一位熱愛數學的男孩子,在茫茫的數字中,他對質數更有一種獨特的情感。小 X 認為,質數是一切自然數起源的地方。
在小 X 的認知裏,質數是除了本身和 1 以外,沒有其他因數的數字。
但由於小 X 對質數的熱愛超乎尋常,所以小 X 同樣喜歡那些雖然不是質數,但卻是由兩個質數相乘得來的數。
於是,我們定義,一個數是小 X 喜歡的數,當且僅當其是一個質數,或是兩個質數的乘積。
而現在,小 X 想要知道,在 L到 R 之間,有多少數是他喜歡的數呢?
輸入格式
第一行輸入一個正整數 Q,表示詢問的組數。
接下來 Q 行。包含兩個正整數 L 和 R。保證L≤R。
輸出格式
輸出 Q 行,每行一個整數,表示小 X 喜歡的數的個數。
思路:
首先,這題看Q的範圍10的5次方,那麽肯定會有預處理的操作,也就是說我們需要把每個數之前有多少滿足條件的數給統計出來,這裏用到了前綴和思想。
接下來是需要求有多少個滿足條件的數,那麽按照這個數據範圍,這個復雜度必須是O(N)級別的,想一想有什麽算法能夠在線性時間內把一個範圍內的素數篩出來呢?
還真的有,那就是歐拉篩,一個優秀的篩法,比埃氏篩的復雜度還優秀。
歐拉篩的原理是每個合數肯定能被它最小的質因數篩去,所以保證了每個數只會被篩一次。
代碼:
1 #include <stdio.h> 2 3 const int maxn = 1e8;4 bool not_prime[maxn+5]; 5 int prime[maxn]; 6 int main() 7 { 8 int cnt = 0; 9 10 for (int i = 2;i <= maxn;i++) 11 { 12 if (!not_prime[i]) 13 { 14 prime[cnt++] = i; 15 } 16 17 for (int j = 0;j < cnt;j++) 18 { 19if (i * prime[j] > maxn) break; 20 21 not_prime[i*prime[j]] = 1; 22 23 if (i % prime[j] == 0) break; 24 } 25 } 26 27 return 0; 28 }
比較關鍵的地方是if (i % prime[j] == 0) break;這個語句。這個語句保證了每個合數只會被篩一次。當i % prime[j] = 0時,那麽i = k * prime[j],那麽i * prime[j+1] % prime[j] = 0,於是i * prime[j+1]這個數其實已經被prime[j]篩掉了,沒有必要繼續篩,後面的同理。
那麽這道題呢,我們還需要知道質數乘以質數的數量,就在一邊篩選的時候一邊標記,假設當前的這個數是素數,那麽把這個數與素數表中的所有數相乘也是滿足條件的數,於是再用一個數組來標記就可以了。
之後我們再用前綴和的思想求一個數之前滿足條件的數有哪些,這樣就能夠在詢問的時候O(1)地回答。
代碼:
1 #include <stdio.h> 2 #include <string.h> 3 4 bool is[10000005]; 5 bool notp[10000005]; 6 7 int prime[1000005]; 8 int num[10000005]; 9 10 int main() 11 { 12 int cnt = 0; 13 14 for (int i = 2;i <= 10000000;i++) 15 { 16 if (!notp[i]) 17 { 18 is[i] = 1; 19 20 prime[++cnt] = i; 21 22 for (int j = 1;j <=cnt;j++) 23 { 24 if (prime[j] * i > 10000000)break; 25 26 notp[i*prime[j]] = 1; 27 28 is[i*prime[j]] = 1; 29 } 30 } 31 else 32 { 33 for (int j = 1;j <=cnt;j++) 34 { 35 if (prime[j] * i > 10000000)break; 36 37 notp[i*prime[j]] = 1; 38 39 if (i % prime[j] == 0) break; 40 } 41 } 42 } 43 44 for (int i = 1;i <= 10000000;i++) 45 { 46 if (is[i]) num[i] = num[i-1] + 1; 47 else num[i] = num[i-1]; 48 } 49 50 int q; 51 52 scanf("%d",&q); 53 54 for (int i = 0;i < q;i++) 55 { 56 int x,y; 57 58 scanf("%d%d",&x,&y); 59 60 printf("%d\n",num[y] - num[x-1]); 61 } 62 63 return 0; 64 }
計蒜客 2017 NOIP 提高組模擬賽(四)Day1 T1 小X的質數 線性篩素數