区间筛法用于快速筛出a到b区间的全部素数,b-a<=1e6。
题目为nefu-2380,原题链接:Problem - 2380 (nefu.edu.cn)
首先给出一个题解的错误代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6;
bool b[N + 5];
bool bprime[N + 5];
LL prime[N + 5];
LL isprime[N + 5];
LL p;
LL cnt;
void init(LL l, LL r)
{
memset(b, 1, sizeof(b));
memset(bprime, 1, sizeof(bprime));
b[0] = b[1] = 0;
for (LL i = 2; i * i < r; i++) {
if (bprime[i]) {
isprime[++cnt] = i;
for (LL j = max(2LL, (l + i - 1) / i) * i; j <= r; j += i) {
b[j - l + 1] = 0;
}
}
for (int j = 1; j <= cnt && i * isprime[j] <= N; j++) {
bprime[i * isprime[j]] = 0;
if (i % isprime[j] == 0) break;
}
}
for (LL i = 1; i <= r - l; i++) {
if (b[i]) prime[++p] = i + l - 1;
}
}
int main()
{
LL l, r;
while (cin >> l >> r) {
p = 0;
memset(prime, 0, sizeof(prime));
//if (l == 1) l++;
init(l, r + 1);
if (p <= 1) cout << "There are no adjacent primes." << endl;
else {
LL dmin = 9999999999;
LL dmax = 0;
LL dx, dy;
for (LL i = 2; i <= p; i++) {
if (dmin > prime[i] - prime[i - 1]) {
dmin = prime[i] - prime[i - 1];
dx = i;
}
if (dmax < prime[i] - prime[i - 1]) {
dmax = prime[i] - prime[i - 1];
dy = i;
}
}
printf("%lld,%lld are closest, %lld,%lld are most distant.\n", prime[dx - 1], prime[dx], prime[dy - 1], prime[dy]);
}
}
return 0;
}
错误原因: 因为要解决当左端点为1时答案出错的情况,所有在init中赋值b[0]和b[1]导致在最后把l到r区间的素数放到prime数组中出现错误
欧拉筛写法(较繁琐):
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6;
bool b[N + 5];//a到b的素数判断
bool bprime[N + 5];//0到sqrt(b)的素数判断数组
LL prime[N + 5];//a到b的素数数组
LL isprime[N + 5];//0到sqrt(b)的素数数组
LL p;
LL cnt;
void init(LL l, LL r)
{
memset(b, 1, sizeof(b));
memset(bprime, 1, sizeof(bprime));
for (LL i = 2; i * i < r; i++) {
if (bprime[i]) {
isprime[++cnt] = i;
for (LL j = max(2LL, (l + i - 1) / i) * i; j <= r; j += i) {
b[j - l + 1] = 0;
//2LL是2的长整型形式
// max用于把((a+i-1)/i)*i)和最小素数2进行比较,一般是((a+i-1)/i)*i),但是但a是1就是2了,它保证删除的都是素数的倍数
// ((a+i-1)/i)*i)当a为1时,如果没有最大值判定,那么j就是1,会把所有数字删掉
// 右移回到a,b区间进行判断,判断成功左移回来赋值false
}
}
for (int j = 1; j <= cnt && i * isprime[j] <= N; j++) {//正常欧拉筛写法
bprime[i * isprime[j]] = 0;
if (i % isprime[j] == 0) break;
}
}
for (LL i = 1; i <= r - l; i++) {//找到了所有a到b的素数,把素数放进素数表中
if (b[i]) prime[++p] = i + l - 1;
}
}
int main()
{
LL l, r;
while (cin >> l >> r) {
p = 0;
memset(prime, 0, sizeof(prime));
if (l == 1) l++;
init(l, r + 1);//r+1是因为区间筛是对[a,b)区间筛所以加一
if (p <= 1) cout << "There are no adjacent primes." << endl;
else {
LL dmin = 9999999999;
LL dmax = 0;
LL dx, dy;
for (LL i = 2; i <= p; i++) {
if (dmin > prime[i] - prime[i - 1]) {
dmin = prime[i] - prime[i - 1];
dx = i;
}
if (dmax < prime[i] - prime[i - 1]) {
dmax = prime[i] - prime[i - 1];
dy = i;
}
}
printf("%lld,%lld are closest, %lld,%lld are most distant.\n", prime[dx - 1], prime[dx], prime[dy - 1], prime[dy]);
}
}
return 0;
}
埃氏筛写法(较简洁):只需要把init函数的欧拉筛相关全部换成埃氏筛即可
void init(LL l, LL r)
{
memset(b, 1, sizeof(b));
memset(bprime, 1, sizeof(bprime));
for (LL i = 2; i * i < r; i++) {
if (bprime[i]) {
for (LL j = i * i; j * j <= r; j += i) {//这里压缩了一下
bprime[j] = 0;
}
for (LL j = max(2LL, (l + i - 1) / i) * i; j <= r; j += i) {
b[j - l + 1] = 0;
}
}
}
for (LL i = 1; i <= r - l; i++) {
if (b[i]) prime[++p] = i + l - 1;
}
}
个人推荐埃氏筛但是由于本人之前使用的的全是欧拉筛,所以第一次用区间筛使用的也是欧拉筛。但是写了一个欧拉筛之后,跟其他人写的对比发现确实麻烦了不少,所以自己又写了一个埃氏筛的方法以作对比。