题意
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();
}
}
}