【题目描述:】
uim在公司里面当秘书,现在有n条消息要告知老板。每条消息有一个好坏度,这会影响老板的心情。告知完一条消息后,老板的心情等于之前老板的心情加上这条消息的好坏度。最开始老板的心情是0,一旦老板心情到了0以下就会勃然大怒,炒了uim的鱿鱼。
uim为了不被炒,知道了了这些消息(已经按时间的发生顺序进行了排列)的好坏度,希望研究如何不让老板发怒。
uim必须按照时间的发生顺序逐条将消息告知给老板。不过uim可以使用一种叫“倒叙”的手法,例如有n条消息,小a可以从k,k+1,k+2...n,1,2...k-1这种顺序通报。
他希望知道,有多少个k,从k开始通报到n然后从1通报到k-1可以让老板不发怒。
【输入格式:】
第一行一个整数n(1 <= n <= 10^6),表示有n个消息。
第二行n个整数,按时间顺序给出第i条消息的好坏度Ai(-1000 <= Ai <= 1000)
【输出格式:】
一行一个整数,表示可行的方案个数。
在旁边wzx大(巨)佬的指点下切掉了这道题
[算法分析:]
对于要判断区间([l, r])内在某一时刻的和是否为负值,只需要看从([l, r])的前缀和序列的值是否为负.
(这道题是要求对于一个点(i),判断([i, n])和([1, i-1])中某一时刻的前缀和是否存在负值)
那怎样快速地求出这个前缀和序列并判断是否有负值呢?
枚举的方法显然并不高效,反而和直接模拟的复杂度一样了,抛掉
判断是否有负值,可以找到一个标准,只要这个标准非负,那整个序列也就没有负值了
显然的,标准应选为([l, r])的前缀和序列的最小值,便可以使用st表或者线段树来快速查询.
那又应该如何求出([l, r])的前缀和序列呢?每次求一遍?显然复杂度会降到约为(O(n^3))
对于一个序列(a),([1, r])的前缀和序列的最小值再减去([1, l - 1])的元素和,即为([l, r])的前缀和序列最小值
即:$$min{sum_{j = l}^{r}sum_{i = l}^{j} a_i} = min{sum_{j = 1}^{r}sum_{i = 1}^{j} a_i} - sum_{i = 1}^{l - 1}a_i$$
所以当我们根据序列(a)求出([l, r])的前缀和序列sum时,使用st表维护这个序列sum的最小值,
就可以做到(O(1))查询(min{sum_{j = l}^{r}sum_{i = l}^{j} a_i})
预处理复杂度(O(nlog_2n)),算法主体的时间复杂度为(O(n))
而使用线段树也可以维护最小值,根据前缀和序列建树,预处理(O(n)),算法主体复杂度为(O(nlog_2n)),和st表差不多。
(但是实测好像st表跑的慢一些)
也可以使用单调队列来求最值,复杂度应该是(O(n)),比st表和线段树都要快,但是我不会QwQ
([Code:])
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 1e6 + 1;
const int K = 20 + 1;
int n;
int a[MAXN], sum[MAXN];
int st[MAXN][K], Log2[MAXN];
inline int read() {
int x=0, f=1; char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch == '-') f = -1;
ch = getchar();
}
while(ch>='0' && ch<='9')
x=(x<<3)+(x<<1)+ch-48, ch=getchar();
return x * f;
}
inline int Query(int l, int r) {
int x = Log2[r - l + 1];
return min(st[l][x], st[r-(1<<x)+1][x]);
}
int main() {
n = read();
for(int i=1; i<=n; ++i) a[i] = read();
for(int i=1; i<=n; ++i)
st[i][0] = sum[i] = sum[i-1] + a[i];
for(int j=1; j<=K; ++j)
for(int i=1; i+(1<<j)-1<=n; ++i)
st[i][j] = min(st[i][j-1], st[i+(1<<(j-1))][j-1]);
for(int i=0; (1<<i)<=n; ++i) Log2[1<<i] = i;
for(int i=1; i<=n; ++i)
if(!Log2[i]) Log2[i] = Log2[i-1];
int ans = 0;
for(int i=1; i<=n; ++i) {
if(Query(i, n)-sum[i-1] < 0) continue;
if(Query(1, i-1) + sum[n] - sum[i-1] >= 0) ++ans;
}
printf("%d
", ans);
}