P2629 好消息,坏消息 题解
user100566 · · 题解
详细地讲一下用前缀和思想 123ms,最大 15ms, 4.36MB 的最优解。
题目分析
给定一个长度为
为了方便处理
考虑某个不合法的
显然,一个
后缀合法的判定
如果某个
用
由上式恒成立,可得出以下等价式:
维护后缀和
事实上并不需要真的维护后缀和数组
前缀合法的判定
如果某个
再定义
由上式恒成立,可得出以下等价式:
维护
但是,前面判定后缀合法时已经进行了优化,没有真正的
还是考虑递推,如果已知
同样的,也不需要真的维护数组
其它优化
可以使用 std::bitset 压缩后缀合法布尔数组的大小,同时不需要维护前缀合法布尔数组:在判定前缀合法时,如果
代码
云剪贴板存档
#include <cstdio>
#include <algorithm>
#include <bitset>
using namespace std;
// 快读
#ifdef ONLINE_JUDGE
#define getchar() getchar_unlocked()
#endif
inline int read(){
int c = getchar();
while(c<45||c>57) c = getchar();
// '-' 的 ASCII 值为 45
if(c==45){
int x = 0;
c = getchar();
while(48<=c&&c<=57){
x = (x<<3)+(x<<1)+(c^48);
c = getchar();
}
return -x;
}else{
int x = 0;
while(48<=c&&c<=57){
x = (x<<3)+(x<<1)+(c^48);
c = getchar();
}
return x;
}
}
// 下标全部从 0 开始
int n, a[1000000];
// 注意 bitset 赋初值 false
bitset<1000000> b(false);
int main(){
n = read();
for(int i=0; i<n; ++i) a[i] = read();
// 后缀和,最大后缀和
int suf = 0, maxsuf = 0;
for(int i=n-1; ~i; --i){
suf += a[i];
// 最小区间和 = suf-maxsuf>=0
if(suf>=maxsuf) b[i] = true;
maxsuf = max(maxsuf, suf);
}
int ans = 0;
// 前缀和,最小前缀和
int pre = 0, minpre = 0;
for(int i=0; i<n; ++i){
// 最小区间和 = suf+minpre>=0
if(b[i] && suf>=-minpre) ++ans;
pre += a[i];
minpre = min(minpre, pre);
suf -= a[i];
}
printf("%d", ans);
return 0;
}