POJ 2689 Prime Distance(大区间素数筛法,两次筛法)

题目链接: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;
}


你可能感兴趣的:(数据结构,算法,素数,ACM,筛法)