#10197. 「一本通 6.2 例 1」Prime Distance
阿新 • • 發佈:2018-12-12
題目描述
原題來自: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; }