1. 程式人生 > >#10197. 「一本通 6.2 例 1」Prime Distance

#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;
}