题目描述
原题来自:Waterloo local,题面详见 POJ 2689
给定两个整数 L,RL,RL,R,求闭区间 [L,R][L,R][L,R] 中相邻两个质数差值最小的数对与差值最大的数对。当存在多个时,输出靠前的素数对。
输入格式
多组数据。每行两个数 L,RL,RL,R。
输出格式
详见输出样例。
样例
样例输入
2 17
14 17
样例输出
2,3 are closest, 7,11 are most distant.
There are no adjacent primes.
数据范围与提示
对于全部数据,1≤L<R<231,R−L≤1061\le L\lt R\lt 2^{31},R-L\le 10^61≤L<R<231,R−L≤106。
l,u范围太大,不能直接求int范围的素数。而区间间隔比较小,只有1e6,而且对于int范围内的合数来说,最小质因子必定小于2^16。所以可以求出[l,u]中合数,转而求出素数,然后暴力枚举所有素数对即可。
因为L,U的值太大了,普通素性判断和素数筛法都不可行,所以可以考虑先筛选
一次,筛出50000以内的素数,然后用50000以内的素数再次筛选出区间【L,U】的素
数。第一次素数筛法比较简单,主要是第二次筛法,分别判断【L,U】中每个数是50000
以内的素数的多少倍,若为1倍,则从2倍开始筛选。若不为1倍,则考虑是否是整数倍,
若为整数倍,则从整数倍开始筛,否则从下1倍开始筛选
比如【L,U】为【50000,60000】,则50000为2的250000倍,应从2的25000倍筛选,
也就是50000,50002,50004,50006……筛去。
若【L,U】为【50001,60000】,则50001不为2的整数倍,应从2的250001倍筛选,也
就是50002,50004,50006……筛去。
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<string>
using namespace std;
bool Prime[50010];
int Primer[1000010];
bool Prime1[1000010];
typedef long long LL;
int IsPrime()//第一次筛50000内的素数
{
int num = 0;
for(int i = 2; i <= 50000; i++)
Prime[i] = true;
for(int i = 2; i <= 50000; i++)
{
if(Prime[i])
{
Primer[num++] = i;
for(int j = i+i; j <= 50000; j+=i)
{
Prime[j] = false;
}
}
}
return num;
//num为50000范围内的素数个数
}
int solve(LL a,LL b)
/*
在第一次筛素数的基础上,利用50000以内的素数,筛去范围【a,b】之间的素数倍数,
剩下则为素数
*/
{
int num = IsPrime();
memset(Prime1,true,sizeof(Prime1));//Prime1数组用来存放范围【a,b】的素性判断
if(a == 1)//这里注意1不是素数
Prime1[0] = 0; //这里表示0+1不为素数
for(LL i = 0; i < num && Primer[i] * Primer[i] <= b; i++)
{
LL begin = a/Primer[i] + (a%Primer[i] != 0);
//上边的a/Primer算出应a为素数Primer[i]的多少倍
//(a%Primer[i]!=0)表示应从Primer[i]的a/Primer[i]倍开始筛,还是a/Primer[i]+1倍筛
if(begin == 1)//若得出结果为所被筛素数的1倍,则从该素数的2倍开始筛
begin++;
for(begin = begin*Primer[i]; begin <= b; begin += Primer[i])
Prime1[begin - a] = false;
}
//这里重新利用Primer数组,用来存放区间【a,b】间的素数,num为素数个数
memset(Primer,0,sizeof(Primer));
num = 0;
for(LL i = a; i <= b; i++)
{
if(Prime1[i-a]==1)
{
Primer[num++] = i-a;
}
}
return num;
}
int main()
{
LL a,b;
LL posa1,posb1,posa2,posb2;
while(~scanf("%I64d%I64d",&a,&b))
{
LL Max = -2147483647,Min = 2147483647;
int num = solve(a,b);
// for(LL i = 0; i < num; i++)
// printf("%I64d ",Primer[i]+a);
if(num <= 1)
{
printf("There are no adjacent primes.\n");
continue;
}
for(int i = 0; i < num-1; i++)//这里i+1范围为1到num-1,则i范围为0到num-2,之前一直错在这里
{
if(Primer[i+1]-Primer[i] < Min)
{
Min = Primer[i+1] - Primer[i];
posa1 = Primer[i];
posb1 = Primer[i+1];
}
if(Primer[i+1]-Primer[i] > Max)
{
Max = Primer[i+1] - Primer[i];
posa2 = Primer[i];
posb2 = Primer[i+1];
}
}
printf("%lld,%lld are closest, %lld,%lld are most distant.\n",posa1+a,posb1+a,posa2+a,posb2+a);
}
return 0;
}