BZOJ 2086: [Poi2010]Blocks

Description

每次可以将大于 \(k\) 的一个数 \(-1\), 在左边或右边的数 \(+1\) ,问最大能得到多长的序列每个数都大于等于 \(k\) .

Sol

单调栈.

这道题好神啊qwq...思路++

首先如果一段序列满足 \(\sum_{i=l}^ri \geqslant k(r-l+1)\) 那么这个序列就是合法的,把所有的数都 \(-k\) 那么只需要满足非负即可.

然后从前往后找一个单调递减的前缀和序列,如果有小于等于前面的某个位置的前缀和,那么选这个不如选前面大于等于他的那个位置.

然后倒着做,右端点递减的时候,左端点要满足递减才能对答案产生贡献,就用一个栈来维护就行.

Code

/**************************************************************
    Problem: 2086
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:4768 ms
    Memory:20820 kb
****************************************************************/
 
#include <bits/stdc++.h>
using namespace std;
 
typedef long long LL;
const int N = 1e6+50;
 
LL n,m,ans;
LL a[N],s[N];
int stk[N],top;
 
inline LL in(LL x=0,char ch=getchar()) { while(ch>'9' || ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x; }
int main() {
    n=in(),m=in();
    for(int i=1;i<=n;i++) a[i]=in();
    for(;m--;) {
        LL x=in();top=0,ans=0;
        for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i]-x;
        for(int i=1;i<=n;i++) if(s[i]<s[stk[top]]) stk[++top]=i;
        for(int i=n;i>=0;i--) {
            while(top && s[i]-s[stk[top-1]]>=0) top--;
            ans=max(ans,(LL)i-stk[top]);
        }printf("%lld%c",ans,"\n "[m>0]);
    }
    return 0;
}

 

posted @ 2016-12-23 21:06  北北北北屿  阅读(167)  评论(0编辑  收藏  举报