題意
LOJ-1197
題意很簡單,求[a,b]之間的素數。但是題目要求 a,b取值範圍是 1 - (2^31)
並且 b-a保證 不大於 100000
思路
看到這麼大的範圍,知道直接篩肯定超時, 又看到人家給了左右區間差, 還是比較小的,因此就想到了區間篩。
區間篩選,人話就是, 利用 1到根號b 之間的素數, 取倍數從而刪除掉a和b之間的非素數。剩下的就是 a到b之間的素數了。如圖
詳細證明可以看《挑戰程序設計》
對於數據範圍,我們需要注意,這個數很巧妙, 我們針對 java來說說。
先看看java的數據類型(複習一下)
這樣看來, 我們可以使用int類型作爲數組下標。
易錯點
但是對於這樣一組數據
2147383647 2147483647
請注意, 我們在篩選的時候要注意, 2147483647 +1 = -2147483648. 正數一下子變成負數, 這意味着循環中很容易造成誤判,比如
for(int j = 2147384648 ; j<2147483647;j+=2)
如果這樣寫, 必定死循環了, 因爲 2147483646 + 2 = -2147483648 ; 完美死循環了。
所以,應該改寫成
for(int j = 2147384648 ; j>0 && j<2147483647;j+=2)
此外, 對於這樣一組數據, 因爲我們一般把右邊界 加上1 ,作爲循環的值,請看代碼。
但是如果右邊界是 2147483647 ,千萬別加上1 了,加上一就成負數了。。。
這時候提前把結果加上1 , 因爲2147483647是質數,再正常運行就行了。
最後,我們統計 a 到 b 的素數時,把 a到b 映射到 0到 b-a 就行了。,,
isp 表示 0 到 b
isp2 表示 0 到 b-a
代碼
import java.io.BufferedInputStream;
import java.util.Scanner;
public class Main {
static boolean[] isp,isp2;
static int[] prim;
static int shai( int a,int b) {
int res=0;
if(b==2147483647) res++;
else b=b+1;
for(int i=0;i<Math.sqrt(b);i++)
isp[i]=true;
isp[0]=isp[1]=false;
for(int i=0;i<b-a;i++)isp2[i]=true;
for(int i=2;i<Math.sqrt(b);i++) {
if(isp[i]) {
//0- 根b 之間
for(int j=i+i;j<Math.sqrt(b);j+=i) isp[j]=false;
//再篩選 a 到 b 之間
//這裏的max是爲了求出 離 a最近的那個非素數
for(int j=Math.max(2, (a-1+i)/i)*i;j>0 &&j<b;j+=i) isp2[j-a]=false;
}
}
if(a==1) isp2[0]=false; // 如果a是1, 特判一下 1是非素數
for(int i=0;i<b-a;i++) //統計 a-b 之間的素數
if(isp2[i]) res++;
return res;
}
public static void main(String[] args) {
Scanner sc=new Scanner(new BufferedInputStream(System.in));
isp=new boolean[1000005];
isp2=new boolean[100005];
prim=new int[10000];
int test=sc.nextInt();
for(int i=1;i<test+1;i++) {
int a =sc.nextInt();
int b=sc.nextInt();
int res=shai(a,b);
System.out.println("Case "+i+": "+res);
System.gc();
}
}
}