埃氏筛法 |
(2)时间复杂度:O(nloglogn)
const int N = 1e6 + 5;
int prime[N];
bool is_prime[N]; //is_prime[i]为true时表示i是素数
int cnt = 0; //素数的个数
void Init(int n)
{
memset(is_prime, true, sizeof(is_prime));
is_prime[0] = is_prime[1] = false; // 0和1不是素数
for (int i = 2; i <= n; i++) {
if (is_prime[i]) {
prime[cnt++] = i;
for (int j = i + i; j <= n; j += i)
is_prime[j] = false; //筛去所有素数的倍数
}
}
}
埃氏筛法无法避免重复地筛一些数,比如说30=2 * 15=3 * 10=5 * 6,所以为了避免重复筛,我们有线性筛法
线性筛法(让每个合数只被它最小的质因子筛选) |
const int N = 1e6 + 5;
int prime[N];
bool vis[N];
void Euler(int n)
{
int cnt = 0;
memset(prime, 0, sizeof(prime));
memset(vis, false, sizeof(vis));
for (int t = 2; t <= n; t++)
{
if (!vis[t]) prime[cnt++] = t;
for (int j = 0; j<cnt&&t*prime[j] <= n; j++)
{
vis[t*prime[j]] = true;
if (t%prime[j] == 0) break; //key point
}
}
}
题目描述
给定 l, r,求区间 [l,r] 内质数的个数。
输入格式:
第一行有两个整数,分别代表询问次数 n 和 给定区间的右端点最大值 m。
接下来 n 行,每行两个整数 l, r,代表一次查询。
输出格式:
对于每次查询输出一行,若 l,r∈[1,m],则输出区间质数个数,否则输出 Crossing the line。
输入样例#1:
2 5
1 3
2 6
输出样例#1:
2
Crossing the line
//线性筛法
#include<cstring>
#include<iostream>
using namespace std;
const int N = 1e6 + 5;
int prime[N];
bool vis[N];
int sum[N];//前缀和
int n, m, l, r;
void Euler(){
int cnt = 0;
memset(prime, 0, sizeof(prime));
memset(vis, false, sizeof(vis));
vis[0] = vis[1] = true; //0和1都不是素数
for (int t = 2; t <= m; t++)
{
if (!vis[t]) prime[cnt++] = t;
for (int j = 0; j<cnt&&t*prime[j] <= m; j++)
{
vis[t*prime[j]] = true;
if (t%prime[j] == 0) break;
}
}
}
//处理前缀和
void Init() {
for (int i = 1; i <= m; i++) {
sum[i] = sum[i - 1] + !vis[i];
}
}
int main() {
cin >> n >> m;
Euler();
Init();
while (n--) {
cin >> l >> r;
if (r > m||l < 1) {
cout << "Crossing the line" << endl;
}
else {
cout << sum[r] - sum[l - 1] << endl;
}
}
return 0;
}