POJ(2689 Prime Distance经典的区间筛选素数!)

题目大意:给出一个区间,长度<=1000 000;求其中素数相邻素数之间的差最小的和最大的(左右端点的最值可以为2147483647);

题目分析:这里因为左右端点的数值过大,所以没办法用常规方法操作! 但是这里 区间的长度才1000000 这样的长度操作难度还是可以接受的 不会超时。

在这里我们先求出能表示2147483647的质因子 其实这个范围不大 就是sqrt(2147483647) 50000以内! 这里我着重解释一下为什么需要求2147483647? 举个例子说明,让你求一个区间a,b里相邻素数的最小、最大差值。
这里我们分两种情况讨论:

                         第一种:就是小数类型的a,b(也就是说b不大于2147483647里的最大质因子)

                                          这里我们只需要枚举,比较过去就可以了,不是吗?
                          第二种:a,b非常之大! 超过了2147483647里的最大质因数的值,这里我们该怎么办呢?

你会怎么做?第一我也跟你一样 ,真不知道如何转换, 您别灰心, 其实这里我们用到就是区间平移。何为区间平移?:就是把在长度为1000000的数组里进行模拟,下标为0的位置上的值   表示a的状态(就是指他是不是素数,"false"代表不是,反之则是素数!),下标为i上的值 表示 a+i  是否为素数!

然后要怎么做呢? 这里其实想通了 就好理解了,任何数都能用素数表示,对吗?如果这个数 不是任何比他小的素数的倍数(这里1没算素数),那么他肯定是素数,有木有?这样的话就好办了,我们只要把用来模拟平移的区间里,所有是2147483647的质因子的倍数全部筛选掉不就行了么?这里还补充一点 为什么是2147483647里面的质因子呢?因为你a,b肯定是比2147483647小,对吧?他的质因子肯定能够包含你的,所以用他的进行筛选,才是完整的满足任何一个在2147483647里面的大数。

这里我格外对代码和注意的地方强调一下!:

第一:for(i=1;i<=k;i++){
                start=l/pri[i]*pri[i];
                if(start<l)
                    start+=pri[i];
                start-=l;
                for(j=start;j<=end;j+=pri[i])
                    res[j]=false;    
            }// l 是(L)的小写形式

这是什么意思呢?start=l/pri[i]*pri[i]

                       就是求出最接近l的 start 能够整除pri[i]的数

                    例如 l=5;pri[i]=3;

                start=l/pri[i]*pri[i] 之后start=3;

               如果 start<l

                    那么start+=pri[i]

                          这里start=6了

             6是不是最接近5 并且能够整除3 的呢?

           懂了吗 ?如果不懂 您就留言吧

还有 一个注意的地方:

这里1不是素数,所以当输入的数第一个为1的时候 要自行加上一个1!不然就是错误的!

代码+注释:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
bool flag[1000001]={0};
int ans[1000001];
int pri[50000];
bool res[1000001];
int k;
void init(){
	int i;
	k=1;
	pri[1]=2;
	for(i=4;i<=50000;i+=2)
		flag[i]=1;
	for(i=3;i<=50000;i+=2){
		if(!flag[i]){
			pri[++k]=i;
			for(int j=2*i;j<=5000;j+=i)
				flag[j]=1;
		}	
	}
	return ;
}
void solve(){
	int i,j,times;
	long  sn1,en1,dis,mind,maxn,l,u,sn2,en2;
	long start,end;
	while(scanf("%ld %ld",&l,&u)!=EOF){
		end=u-l;
		if(l==1)
			l++;
		if(u<pri[k]){
			times=0;
			for(i=l;i<=u;i++)
				if(!flag[i])
					ans[++times]=i;
		}
		else{
			for(j=0;j<=end;j++)
				res[j]=true;
			for(j=(l%2!=0);j<=end;j+=2)
				res[j]=false;
			for(i=1;i<=k;i++){
				start=l/pri[i]*pri[i];
				if(start<l)
					start+=pri[i];
				start-=l;
				for(j=start;j<=end;j+=pri[i])
					res[j]=false;	
			}
			times=0;
			for(i=0;i<=end;i++)
				if(res[i])
					ans[++times]=i+l;
                                     //这里记得复原 移动的区//间哦题目要求的是区间,所以您得记得复原区间哦
		}
		mind=2147483647;
		maxn=0;
		for(i=1;i<times;i++){
			if(mind>ans[i+1]-ans[i]){
				sn1=ans[i];
				en1=ans[i+1];
				mind=ans[i+1]-ans[i];
			}
			if(maxn<ans[i+1]-ans[i]){
				sn2=ans[i];
				en2=ans[i+1];
				maxn=ans[i+1]-ans[i];
			}
		}
		if(maxn==0){
			printf("There are no adjacent primes.\n");
			continue;
		}
		printf("%ld,%ld are closest, %ld,%ld are most distant.\n",sn1,en1,sn2,en2);
	}
}
int main(){
	init();
	solve();
	return 0;
}