• 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;
    }
    
  • 相关阅读:
    京东分页
    相册分类列表页
    在线运行Javascript,Jquery,HTML,CSS代码
    点击事件后动画提示
    一些广告代码
    爱可有—之最经典
    爱可有网络社区需要定义
    鼠标移动时缩小图片显示说明
    Flask-RESTful 快速入门
    Sequelize 关系模型简介
  • 原文地址:https://www.cnblogs.com/zhwer/p/14611110.html
Copyright © 2020-2023  润新知