题目

思路
解题思路
1. 问题分析
- 核心需求:给定多组查询区间
[a, b],统计区间内孪生素数对(即两个素数相差 2,如 (3,5)、(5,7))的数量。 - 关键:孪生素数对的形式是
(x, x+2),因此统计的本质是找满足a ≤ x ≤ b-2且x和x+2都是素数的x的个数。
2. 核心难点与解决思路
- 难点:如果对每个查询都逐个判断数是否为素数,时间复杂度会很高(尤其是数据范围到 500 万),无法高效处理多组查询。
- 解决思路:预处理 + 前缀和 (空间换时间):
- 先用筛法一次性找出所有素数(预处理);
- 再用前缀和数组记录前 i 个数中孪生素数对的总数;
- 每个查询直接通过前缀和数组的差值快速得到结果。
3. 具体实现步骤
步骤 1:素数筛选(埃拉托斯特尼筛法)
- 目的:标记出 500 万以内所有非素数,为后续判断孪生素数做准备。
- 操作:
- 定义布尔数组
st[N+1],st[i] = true表示 i 不是素数,初始全为false; - 遍历 i 从 2 到 N:若 i 未被标记(是素数),则标记 i 的所有倍数(从 i*i 开始)为非素数。
- 定义布尔数组
步骤 2:孪生素数判断
- 目的:判断一个数 x 是否是某对孪生素数的较小数(即 x 和 x+2 都是素数)。
- 操作:
- 检查条件:
x ≤ N-2(保证 x+2 不超范围)、!st[x](x 是素数)、!st[x+2](x+2 是素数)。
- 检查条件:
步骤 3:前缀和数组构建
- 目的:构建数组
f[N+1],其中f[i]表示 2~i 范围内孪生素数对的总数。 - 操作:
- 初始化
f[0] = f[1] = 0(小于 2 无素数); - 遍历 i 从 2 到 N:
- 若 i 是孪生素数对的较小数,
f[i] = f[i-1] + 1; - 否则,
f[i] = f[i-1](总数不变)。
- 若 i 是孪生素数对的较小数,
- 初始化
步骤 4:查询处理
- 目的:对每组查询
[a, b],快速计算结果。 - 操作:
- 边界处理:若
a > b-2或b ≤ 3,结果为 0(无符合条件的孪生素数对); - 正常情况:结果 =
f[b-2] - f[a-1](前缀和差值,即区间 [a, b-2] 内的孪生素数对数量)。
- 边界处理:若
4. 主函数流程
- 先执行素数筛选和前缀和数组构建(仅执行一次);
- 读入查询次数 K,循环处理每组查询,调用查询逻辑并输出结果。
总结
- 核心思想:用筛法预处理素数 + 前缀和快速查询,将多次查询的时间复杂度从 O (n) 降为 O(1);
- 关键逻辑:孪生素数对
(x, x+2)的统计转化为对 x 的范围判断,再通过前缀和差值计算区间数量; - 效率优势:预处理仅需 O (N log log N) 时间,后续每个查询都是 O (1),适合大数据范围和多组查询场景。
代码
cpp
#include<stdio.h>
#include<stdbool.h>
#define ll long long
#define N 5000000
int cnt=0,ans,f[N+1];
bool st[N+1]={false};
void sieve(){
ll i,j;
for(i=2;i<=N;i++){
if(st[i]) continue;
for(j=i*i;j<=N;j+=i)
st[j]=true;
}
}
bool check(int x){
return (x<=N-2)&&(!st[x])&&(!st[x+2]);
}
void prefix(){
f[0]=f[1]=0;
int i;
for(i=2;i<=N;i++){
if(check(i)) f[i]=f[i-1]+1;
else f[i]=f[i-1];
}
}
void solves(int a,int b){
if(a>b-2||b<=3) ans=0;
else ans=f[b-2]-f[a-1];
printf("%d\n",ans);
}
int main(){
sieve();
prefix();
int K,a,b;
scanf("%d",&K);
while(K--){
scanf("%d%d",&a,&b);
solves(a,b);
}
}