Help Hanzo
Amakusa, the evil spiritual leader has captured the beautiful princess Nakururu. The reason behind this is he had a little problem with Hanzo Hattori, the best ninja and the love of Nakururu. After hearing the news Hanzo got extremely angry. But he is clever and smart, so, he kept himself cool and made a plan to face Amakusa.
Before reaching Amakusa’s castle, Hanzo has to pass some territories. The territories are numbered as a, a+1, a+2, a+3 … b. But not all the territories are safe for Hanzo because there can be other fighters waiting for him. Actually he is not afraid of them, but as he is facing Amakusa, he has to save his stamina as much as possible.
He calculated that the territories which are primes are safe for him. Now given a and b he needs to know how many territories are safe for him. But he is busy with other plans, so he hired you to solve this small problem!
Input
Input starts with an integer T (≤ 200), denoting the number of test cases.
Each case contains a line containing two integers a and b (1 ≤ a ≤ b < 231, b - a ≤ 100000).
Output
For each case, print the case number and the number of safe territories.
Sample Input
3
2 36
3 73
3 11
Sample Output
Case 1: 11
Case 2: 20
Case 3: 4
Note
A number is said to be prime if it is divisible by exactly two different integers. So, first few primes are 2, 3, 5, 7, 11, 13, 17, …
题意:
求a~b区间素数的数量。(1 ≤ a ≤ b < 2^31, b - a ≤ 100000).
思路:
由于a,b的取值范围太大,无法将这个区间内的所有素数全部略举出来,但b-a这个范围相对小了很多。
我们需要通过打表找到 [2,sqtr(b)]之间的素数,就可以把埃氏筛法运用到【a,b】之间,
素数。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn= 1e5+10;
#define ll long long
bool prime1[maxn],prime2[maxn];
ll i,j;
int sieve(ll a,ll b)
{
for(i=0;i*i<=b;i++)
prime1[i]=0;
for(i=0;i<=b-a;i++)
prime2[i]=1;
for(i=2;i*i<=b;i++)
{
if(prime1[i]==0)
{
for(j=i+i;j*j<=b;j+=i)
prime1[j]=1;
for(j=max(2ll,(a+i-1)/i)*i;j<=b;j+=i)
prime2[j-a]=0;//这里的2LL是2的长整型数,与2LL比较的意思就是j最小是i的两倍
//(a+i-1)/i表示的是[a,b]区间内的第一个数至少为i的多少倍.
}
}
int sum=0;
for(int i=0;i<=b-a;i++)
if(prime2[i])sum++;
return sum;
}
int main()
{
ll a,b;
int t;
int ans=1;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&a,&b);
int res=sieve(a,b);
if(a==1)res--;//因为上面函数初始化的时候把所有的数都定义为素数,后来经过for循环
// 4,6,8....等数都成了false,但是1还是true,所以当a等于1的时候,
// 要减去1
printf("Case %d: %d\n",ans++,res);
}
return 0;
}
埃氏筛法:
原理其实很简单,首先将2到n范围内的整数写下来,其中2是最小的素数。将表中所有的2的倍数划去,表中剩下的最小的数字就是3,他不能被更小的数整除,所以3是素数。再将表中所有的3的倍数划去……以此类推,如果表中剩余的最小的数是m,那么m就是素数。然后将表中所有m的倍数划去,像这样反复操作,就能依次枚举n以内的素数,这样的时间复杂度是O(nloglogn)。
这个里面还有一个小技巧,就是加入我们知道了当前的质数,我们要怎么知道下一个质数。我们可以发现,加入我们标记了当前质数的所有比它大的倍数之后我们从当前质数开始往下枚举,遇到第一个没有被标记的数就是下一个质数了。
原因:
因为往后找第一个没有标记过的数说明了首先这个数是一个质数,因为这个数没有被标记说明了这个数与之前的那个质数互质,由于这个数与质数互质,所以这个数当然就一定是质数了。而且这个数是往后第一个与当前质数互质的数,所以我们可以知道这个数是往后的第一个质数,因为之前的数都是当前质数的倍数。所以之前的数都是合数。所以这个数一定是当前质数往后遇到的第一个质数,也就是下一个质数了。
代码:
int prime[max_n];//第i个素数
bool is_prime[max_n+1]//is_prime[i]为ture表示i是素数
//返回n以内素数的个数
int sieve(int n){
int p=0;
for(int i=0;i<=n;i++) is_prime[i]=ture;
is_prime[0]=is_prime[1]=false;
for(int i=2;i<=n;i++){
if(is_prime[i]){
prime[p++]=i;
for(int j=2*i;j<=n;j+=i) is_prime[j]=false;
}
}
return p;
}
基本实现:
首先,列出从2开始的所有自然数,构造一个序列:
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, …(加粗为删除的意思)
取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉:
3,4, 5,6, 7,8, 9,10, 11,12, 13,14, 15,16, 17,18, 19,20, …
取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉:
5,6, 7,8,9,10, 11,12, 13,15,16, 17,18, 19,20, …
取新序列的第一个数5,然后用5把序列的5的倍数筛掉:
7,8,9,10, 11,12, 13,14,15,16, 17,18, 19,20, …
.不断筛下去,就可以得到所有的素数。