Luogu2689 | 好消息,坏消息(单调队列)

题目描述

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))

输出格式

一行一个整数,表示可行的方案个数。

输入输出样例

输入 #1

4
-3 5 1 2

输出 #1

2

说明/提示

样例解释

([5 1 2 -3])([1 2 -3 5])

对于 (25\%) 数据 (n<=1000)

对于 (75\%) 数据 (n<=10000)

对于 (100\%) 数据 (n<=10^6)

————————————————————————————————

对于这道题,显然 (k)(n) 种可能的情况,我们只要对每个 (k) 判断是否合法即可

但如果暴力枚举每一个k,时间复杂度是 (O(n^2))的,只有 (25) 分,需要考虑优化

在考虑优化前,我们考虑 拆环为链 的简化方法,即把样例的 (-3;5;1;2) 补充成为 (-3;5;1;2;-3;5;1)

这样对于每一个起点 (k),我们只需要考虑往后的元素了,把两段的汇报合并成了一段长度为 (n) 的完整汇报

然后我们考虑 老板发怒 的状态是什么,即从 (k) 开始向后统计前缀和,前缀和出现负值的情况则会发怒

于是题目就转换为了:有多少个 (k),从 (k) 开始逐个统计长度为 (1,2,..,n) 的前缀和,这些前缀和都值都不是负数

在实际处理中,我们可以首先计算出完整序列的前缀和 (sum)(sum[i]-sum[k-1]) 就是 (k o i(k<=i<=n+k-1)) 的上述前缀和了

由于我们只关心最小的那个 (sum[i]-sum[k-1]),我们可以使用一个单调队列来维护 (sum[i]),时间复杂度为 (O(n))

对于每一段区间的最小值,最后要再减去 (sum[k-1]) 观察是否为负值,如果不为复制,则说明这个 (k) 是合法的

代码如下

感觉这份代码比较难懂,因为我自己写的时候一直在细节上改233 还是请关注解题思路。

#include <bits/stdc++.h>
#define MAXN 2000007
using namespace std;
int n,ans,sum[MAXN],f[MAXN];
deque<int> Q; vector<int> v; 
inline int readint() {
	int X=0,w=0; char ch=0;
	while (!isdigit(ch)) w|=ch=='-',ch=getchar();
	while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X; 
} 
int main() {
	n=readint();
	for (int i=1;i<=n;i++) v.push_back(readint());
	for (int i=0;i<n-1;i++) v.push_back(v[i]);
	for (int i=0;i<(int)v.size();i++) sum[i]=(i?sum[i-1]:0)+v[i];
	for (int i=0;i<(int)v.size();i++) {
		if (!Q.empty() && Q.front()+n<=i) Q.pop_front();
		while (!Q.empty() && sum[i]<=sum[Q.back()])
			Q.pop_back();
		Q.push_back(i);
		if (i-n+1>=0) f[i-n+1]=sum[Q.front()]-((i==n-1)?0:sum[i-n]);
	}
	for (int i=0;i<n;i++) if (f[i]>=0) ans++;
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/zhwer/p/14611110.html