1. 程式人生 > >計蒜客 2017 NOIP 提高組模擬賽(四)Day1 T1 小X的質數 線性篩素數

計蒜客 2017 NOIP 提高組模擬賽(四)Day1 T1 小X的質數 線性篩素數

範圍 線性篩 mat 需要 接下來 包含 能夠 數字 bottom

小 X 是一位熱愛數學的男孩子,在茫茫的數字中,他對質數更有一種獨特的情感。小 X 認為,質數是一切自然數起源的地方。

在小 X 的認知裏,質數是除了本身和 1 以外,沒有其他因數的數字。

但由於小 X 對質數的熱愛超乎尋常,所以小 X 同樣喜歡那些雖然不是質數,但卻是由兩個質數相乘得來的數。

於是,我們定義,一個數是小 X 喜歡的數,當且僅當其是一個質數,或是兩個質數的乘積。

而現在,小 X 想要知道,在 LR 之間,有多少數是他喜歡的數呢?

輸入格式

第一行輸入一個正整數 Q,表示詢問的組數。

接下來 Q 行。包含兩個正整數 LR。保證LR。

輸出格式

輸出 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 { 19
if (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的質數 線性篩素數