题目链接:http://poj.org/problem?id=2689
题意:求一个区间 [L,U] 内的差值最大的和差值最小的相邻素数对。(1<=L< U<=2,147,483,647),区间长度U-L<=1000000
题解:
维基百科:埃拉托斯特尼筛法
单纯打表是不行的,L,U的范围太大,不能直接求出所有素数。对于int范围内的合数来说,最小质因子必定小于2^16。所以先用筛法选出50000内的素数即可,因为50000的平方大于int范围了。再用这些素数去筛出U-L之间的合数,剩下的就是U-L之间的素数了。然后依次比较求出相邻素数的最大值和最小值即可。二次筛法。。。
区间长度只有1e6,所以在存的时候虽然不能直接存每个数,但是可以加个偏移量L,这样便可存的下。
注意1既不是素数也不是合数。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int MAX=50000; int vis[MAX]; int prim[MAX]; typedef long long LL; int t; void init() { memset(vis,0,sizeof(vis)); vis[1]=1; for(int i=2;i<=(int)sqrt(MAX+0.5);i++) { if(!vis[i]) for(int j=i*i;j<MAX;j+=i) { vis[j]=1; } } t=0; for(int i=2;i<MAX;i++) { if(!vis[i]) { prim[t++]=i; // cout<<i<<endl; } } } const int LMAX=1000010; int prim2[LMAX]; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); init(); int l,u; while(scanf("%d%d",&l,&u)!=EOF) { if(l==1) l=2; memset(prim2,0,sizeof(prim2)); /* for(LL i=l;i<=u;i++) { for(int j=0;(LL)prim[j]*(LL)prim[j]<=(LL)i;j++) { if(i%prim[j]==0) { prim2[i-l]=1; break; } } }*///开始时用的这样的方法但是超时了。。。 for(int i=0;i<t;i++) { int a=(l-1)/prim[i]+1; int b=u/prim[i]; for(int j=a;j<=b;j++) { if(j>1) prim2[j*prim[i]-l]=1; } } int pre=-1; int mindis=LMAX,maxdis=-1; int mins,mine,maxs,maxe; for(int i=0;i<=u-l;i++) { if(prim2[i]==0) { // printf("%d\n",i+l); if(pre==-1) { pre=i; continue; } if(mindis>i-pre) { mindis=i-pre; mins=pre+l; mine=i+l; } if(maxdis<i-pre) { maxdis=i-pre; maxs=pre+l; maxe=i+l; } pre=i; } } if(maxdis==-1) printf("There are no adjacent primes.\n"); else printf("%d,%d are closest, %d,%d are most distant.\n",mins,mine,maxs,maxe); } return 0; }