#10197. 「一本通 6.2 例 1」Prime Distance

题目描述

原题来自: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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值