給定整數a和b,請問區間[a,b)內有多少個素數?
a<b<=10^12
b-a<=10^6
因為b以內合數的最小質因數一定不超過sqrt(b),如果有sqrt(b)以內的素數表的話,就可以把篩選法用在[a,b)上了,先分別做好[2,sqrt(b))的表和[a,b)的表,然后從[2,sqrt(b))的表中篩得素數的同時,也將其倍數從[a,b)的表中划去,最后剩下的就是區間[a,b)內的素數了。
有的時候需要求出某個特定區間的素數,但是數可能很大,數組也開不小,所以需要進行下標偏移,這樣才可以使用篩選法。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int maxn = 1000005; 7 bool is_prime[maxn]; 8 bool is_prime_small[maxn]; 9 ll prime[maxn]; 10 ll prime_num=0; 11 12 //對區間[a,b)內的整數執行篩法,is_prime[i-a]=true --- 表示i是素數 注意這里下標偏移了a,所以從0開始。 13 void segment_sieve(ll a,ll b) { 14 for(ll i=0;i*i<b;++i) is_prime_small[i]=true; //對[2,sqrt(b))的初始化全為質數 15 for(ll i=0;i<b-a;++i) is_prime[i]=true; //對下標偏移后的[a,b)進行初始化 16 17 for(ll i=2;i*i<b;++i) { 18 if(is_prime_small[i]) { 19 for(ll j=2*i;j*j<b;j+=i) is_prime_small[j]=false; //篩選[2,sqrt(b)); 20 //(a+i-1)/i得到最接近a的i的倍數,最低是i的2倍,然后篩選 21 for(ll j=max(2LL,(a+i-1)/i)*i;j<b;j+=i) is_prime[j-a]=false; 22 } 23 } 24 for(ll i=0;i<b-a;++i) //統計個數 25 if(is_prime[i]) prime[prime_num++]=i+a; 26 } 27 28 int main() 29 { 30 ll a,b; 31 while(~scanf("%lld%lld",&a,&b)) 32 { 33 prime_num=0; 34 memset(prime,0,sizeof(prime)); 35 segment_sieve(a,b); 36 //for(ll i=0;i<prime_num;++i) printf("%lld\n",prime[i]); 37 printf("%lld\n",prime_num); 38 } 39 return 0; 40 }