1. 程式人生 > >POJ 2689 Prime Distance [篩法選取素數]【數論】

POJ 2689 Prime Distance [篩法選取素數]【數論】

題目連結:http://poj.org/problem?id=2689
————————-.
Prime Distance
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 16728 Accepted: 4450
Description

The branch of mathematics called number theory is about properties of numbers. One of the areas that has captured the interest of number theoreticians for thousands of years is the question of primality. A prime number is a number that is has no proper factors (it is only evenly divisible by 1 and itself). The first prime numbers are 2,3,5,7 but they quickly become less frequent. One of the interesting questions is how dense they are in various ranges. Adjacent primes are two numbers that are both primes, but there are no other prime numbers between the adjacent primes. For example, 2,3 are the only adjacent primes that are also adjacent numbers.
Your program is given 2 numbers: L and U (1<=L< U<=2,147,483,647), and you are to find the two adjacent primes C1 and C2 (L<=C1< C2<=U) that are closest (i.e. C2-C1 is the minimum). If there are other pairs that are the same distance apart, use the first pair. You are also to find the two adjacent primes D1 and D2 (L<=D1< D2<=U) where D1 and D2 are as distant from each other as possible (again choosing the first pair if there is a tie).
Input

Each line of input will contain two positive integers, L and U, with L < U. The difference between L and U will not exceed 1,000,000.
Output

For each L and U, the output will either be the statement that there are no adjacent primes (because there are less than two primes between the two given numbers) or a line giving the two pairs of adjacent primes.
Sample Input

2 17
14 17
Sample Output

2,3 are closest, 7,11 are most distant.
There are no adjacent primes.

—————————————.

題目大意:
就是給定一個區間L,U∈[1,2147483647] U-L∈[1 000 000] 讓你求出這個區間內距離最近和距離最遠的兩個素數對

解題思路 :
看似不難的題目因為給了[1,2147483647] 這麼一個數據範圍變得似乎不可解了
其實並不難 至少出題人還給了區間範圍≤1 000 000 這麼人性化的一個設定

我們考慮的事求出區間內的素數
其實也就是求出區間內的合數 剩下的就是素數了
這道題目顯然不能用樸素的素數測試了 然後想到篩法 但是篩法並不能篩[1,2147483647] 這麼大範圍的素數
這時候根據樸素測試想到選取素數只需要選取其根號下的範圍即可
這樣範圍就縮小到[1,(1<<16)] 這麼小的範圍了 然後篩法就行了

最後要確定的是[L,U]區間內的素數個數 同樣 我們只要晒出合數就行了
篩的程式碼也很好實現

for(LL i=0; i<k; i++)
{
    b=l/prime[i];
    while(b*prime[i]<l||b<=1) b++;
    for(LL j=b*prime[i]; j<=r; j+=prime[i]) //注意這裡的j一定要用LL  否則資料溢位會出現死迴圈...
        if(j>=l)    is_prime2[j-l]=false;
}

上述程式碼應該很好理解 就是找[1,(1<<16)] 範圍內質數的倍數 也就是[L,U]範圍內的合數 其實就是篩法選素數 一樣的

然後遍歷一下 把[L,U]內的素數記錄下來
最後在遍歷一下 找一下符合題意的素數對就行了

當然上述兩行是可以在一次遍歷中實現的 有興趣的話 可以自己實現一下

附本題程式碼
————————–.

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long int LL ;
#define INF 0x3f3f3f3f
#define pb push_back

#define lalal puts("*******");
/*************************************/

int prime[50050],k=0;
bool vis[(1<<17)+20];

void Prime(  )
{
    memset(vis,true,sizeof(vis));
    vis[0]=vis[1]=false ;
    int Max = (1<<17)+10;
    for (int i=2; i<=Max; i++)
    {
        if(vis[i])   prime[k++]=i;
        for(int j=0; j<k&&i*prime[j]<=Max; j++)
        {
            vis[i*prime[j]]=false;
            if (!( i % prime[j])) break;
        }
    }
}

bool is_prime2[1010101];
LL      prime2[1010101];
int main()
{
    Prime();
    LL l,r;
    while(cin>>l>>r)
    {
        memset(is_prime2,true,sizeof(is_prime2));
        LL b;
        for(LL i=0; i<k; i++)
        {
            b=l/prime[i];
            while(b*prime[i]<l||b<=1) b++;
            for(LL j=b*prime[i]; j<=r; j+=prime[i])
                if(j>=l)    is_prime2[j-l]=false;
        }


        int num=0;
        for(int i=0; i<=r-l; i++)
            if(is_prime2[i]&&i+l>=2) prime2[num++]=i+l;

        if(num<2)    printf("There are no adjacent primes.\n");
        else
        {
            LL minl,minr,maxl,maxr;
            LL mini=INF,maxi=-1;
            for(int i=1; i<num; i++)
            {
                if(prime2[i]-prime2[i-1]<mini)
                    minr=prime2[i],minl=prime2[i-1],mini=prime2[i]-prime2[i-1];
                if(prime2[i]-prime2[i-1]>maxi)
                    maxr=prime2[i],maxl=prime2[i-1],maxi=prime2[i]-prime2[i-1];
            }
            printf("%I64d,%I64d are closest, %I64d,%I64d are most distant.\n",minl,minr,maxl,maxr);
        }
    }
    return 0;
}