题目描述
输入描述
输出描述
输入样例
10
-2
-1
0
1
2
3
4
5
6
7
输出样例
6
4
3
2
1
1
2
1
2
1
题目大意:给定一个 x ,现需要找一个包含 x 的区间 [ l , r ] ,使得该区间内各元素之和为质数,求满足要求的区间的最小长度。
通过输入样例发现,给出的自然数的答案均为 1 or 2,即当前数为质数时长度为 1,当前数为约数时,可与前一位或后一位组成质数。因此不妨先通过打表验证该规律是否满足于所有自然数:
可以发现1 ~ 30 的打表内即出现了不符合规律的 25、28,而通过计算也不难发现,25与前面 1 ~ 24 的任意一段区间之和都不能满足为素数的条件,即:25 + 24 , 25 + 24 + 23 , … ,25 + 24 + … + 1 间的任意一个组合都为约数。
此时感激万能群友提供的仅有区间长度小于等于 2 时,区间和才可能为质数的结论,证明如下:
当区间长度为 1 时,区间和即为 x ;
当区间长度为 2 时,区间和为 2 x + 1 ( 此处假定 x 为该区间内的最小数,下同 );
当区间长度为 3 时,区间和为 3 x + 3 ;
当区间长度为 4 时,区间和为 4 x + 6 ;
……
由于( 3 x + 3 )存在因子 3 ,( 4 x + 6 )存在因子 2 ,因此由该规律不难推出,仅当区间长度小于等于 2 时,区间和才可能为质数。所以对于自然数,可分为三种情况进行讨论:
①该数本身为质数,输出 1 即可;
②该数与相邻的前一位或后一位可组合成质数,输出 2 即可;
③以上两种条件均不符合,特殊处理。
而关于特殊处理的方式,我们可以通过 1 ~ 30 中的 25 和 28 进行讨论。由于区间长度不能大于 2 ,且当前数与前后位组成都不是质数,因此可以考虑将该自然数不断转化为 x + 1 ,直至转换后的数符合之前的 ① ② 条件中的一种。如:
对于 25,可使区间为 [ -25 , 26 ] ,则当前区间和为 26,而 26 可与 27 组合满足条件 ②,因此该样例的最终区间为 [ -25 , 27 ] ,长度为 25 * 2 + 3;
对于 28,可使区间为 [ -28 , 29 ] ,则当前区间和为 29,而 29 为质数满足条件 ①,因此该样例最终区间即为 [ -28 , 29 ] ,长度为 28 * 2 + 2;
而对于负数,可以直接映射到正数区间,即直接令 x = - x 并做以上的特殊处理即可。
参考代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2e7+10;
typedef long long LL;
//线性筛
int vis[N], primes[N], cnt;
void _primes(int n){
vis[0]=vis[1]=1;
for(int i = 2; i < n; i++){
if(!vis[i])primes[++cnt]=i;
for(int j = 1; primes[j] <= n/i; j++){
vis[primes[j]*i] = 1;
if(i%primes[j]==0)break;
}
}
}
int x;
int main(){
_primes(N);
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&x);
if(x>0 && !vis[x]){cout<<1<<endl;continue;}
if(x>0 && !vis[x+x+1]){cout<<2<<endl;continue;}
if(x>0 && !vis[x+x-1]){cout<<2<<endl;continue;}
if(x<0)x=-x;
for(int i = x+1; ;i++){
if(!vis[i]){cout<<(i-1)*2+2<<endl;break;}
else if(!vis[i+i+1]){cout<<(i-1)*2+3<<endl; break;}
}
}
return 0;
}