连续素数之和的问题,可以先把数据范围内的素数列出来,求取这些素数的前缀和,但是10000以内的素数,普通方法判断一个数是否是素数复杂度
O
(
n
)
O(n)
O(n)加上10000个数,感觉会TLE,没有尝试。此时可以
- 采取埃及筛法枚举10000以内的素数。埃及筛法简介
- 求取这些素数集合的前缀和,简单来看 s u m [ j ] − s u m [ i ] = k sum[j]-sum[i]=k sum[j]−sum[i]=k,说明 k k k可以使用 i + 1 − j i+1-j i+1−j的素数集合表示。
- 使用尺取法以 O ( n ) O(n) O(n)的复杂度判断有多少种 i − j i-j i−j组合。
对与尺取法之前讲过,专栏也有几道题尺取法简介,主要解决的问题有四个:
1、 什么情况下能使用尺取法?(尺取法作用的区间是否单调或者满足特殊性质)
2、如何推进区间的端点?
3、如何利用当前区间更新结果值?
4、何时结束区间的枚举?
依次回答这四个问题就行了尺取的整个代码
- 前缀和数组显然是单调的,比较适合尺取法的开展。
- 计所求数字为 k k k,如果 s u m [ j ] − s u m [ i ] > k sum[j]-sum[i]>k sum[j]−sum[i]>k,说明区间和太大了,左端点可以右移。如果 s u m [ j ] − s u m [ i ] < k sum[j]-sum[i]<k sum[j]−sum[i]<k,说明区间和太小,右端点右移。如果相等,那么这段连续素数可以表示 k k k,左右端点同时右移。为了保证左端点不超过右端点,加入语句 i f ( l = = r ) r + + ; if (l == r) { r++; } if(l==r)r++;
- 只有当区间之和等于 k k k的时候,才需要更新结果值,计数+1.
- 当 r r r或者 l l l超过前缀和数组的最后一个元素时,结束枚举。
#include<iostream>
#include<cmath>
#include<iomanip>
#include<vector>
#include<map>
#include<queue>
#include<algorithm>
#include<string.h>
using namespace std;
#define ll int
#define Max 100050
ll n;
ll prime[10005];
int main() {
for (ll i = 2; i <= 10001; i++) prime[i] = true;
prime[0] = prime[1] = false;
for (ll i = 2; i <= 10001; i++) {
for (ll j = i * 2; j <= 10001; j += i) prime[j] = false;
}
vector<ll> p; p.push_back(0);//素数的前缀和数列
for (ll i = 2; i <= 10001; i++)
{
if (prime[i])p.push_back(p[p.size() - 1] + i);
}
while (cin >> n) {
if (n == 0)break;
ll l = 0, r = 1, res = 0;
while (l < p.size() && r < p.size()) {
ll val = p[r] - p[l];
if (val > n) l++;
else if (val < n) r++;
else {
res++; l++; r++;
}
if (l == r) { r++; }
}
cout << res << endl;
}
}