Description
给定两个非负整数a,b,其中0<= a,b<=1,000,000,请计算这两个数之间有多少个素数。
输入
第一行是一个整数K(1<=K<=1000),表示有多少个样例,每个样例占一行,是两个整数a和b,每个整数之间用一个空格隔开。
输出
每行输出一个样例的结果。
Sample Input
2 2 3 17 19Sample Output
2 2
解题思路:
像这类 “求一个区间中有多少个符合条件的数” 的问题,一般考虑用 前缀和 的方法求解。
何为前缀和,举个不恰当的例子:字符串 abcdefghigklmn,a的前缀和是 a,b的前缀和是 a+b,……, f 的前缀和是 a+b+c+d+e+f。
放在数组中,前缀和就是 Sa[i] = a[0]+a[1]+a[2]+ ······ +a[i]。
所以这个题转化成前缀和的方法:0~b之间素数的个数 减去 0~a-1之间素数的个数,就是所求a,b之间素数的个数。
现在的问题就是判断 每个数是否为 素数,这里有个非常强大的筛选素数方法——欧拉筛(线性筛),所有数字只要遍历一遍就能判断出其是否为素数。
代码中 isPrime() 函数 就是 欧拉筛 的代码实现。(除了第15行,这行是结合题目加的)
利用欧拉筛,我们得到了一个books[]数组,books[i] = 1表示 i 为 素数, books[i] = 0 为非素数。现在对 books数组进行前缀和累加,得到 表示数字i之前有多少个素数的 books数组。(36、37行)
现在所有的问题迎刃而解,不管你怎么问,怎么变化a,b的值,我都能够一秒给出答案:books[b] - books[a-1]。
AC代码:
#include <stdio.h>
const int MAXN = 1e6;
bool vis[MAXN+2];
int prime[80000];
int books[MAXN+2];
void isPrime() //欧拉筛(线性筛)
{
for (int i = 2; i < MAXN; i ++)
{
if ( !vis[i])
{
prime[++prime[0]] = i;
books[i] = 1; // 数字i为素数记为1,非素数默认为0
}
for (int j = 1; j <= prime[0] && i <= MAXN/prime[j]; j ++)
{
vis[i*prime[j]] = 1;
if (i % prime[j] == 0)
break;
}
}
}
void swap(int &x, int &y)
{
int t = x;
x = y;
y = t;
}
int main()
{
isPrime();
for (int i = 1; i <= MAXN; i ++) // 0到数字i间一共有多少个 素数
books[i] += books[i-1];
int k,a,b;
scanf("%d",&k);
while (k --)
{
scanf("%d %d",&a,&b);
if (a > b) swap(a,b); // 注意题目没限定a b 的大小关系
printf("%d\n",books[b]-books[a-1]);
}
return 0;
}