bzoj2086 Blocks

题目链接

题面

bzoj2086 Blocks_第1张图片

思路

可以发现其实就是询问一个最长的区间,使得这个区间的平均数大于等于k。所以将区间内所有数字减去k,然后做一遍前缀和。只要是前缀和之差大于等于0的区间。就是满足条件的。
所以现在问题就成了对于前缀和上的每个数字,找到一个最靠前的比他小的数字。
这个可以用单调栈。可以发现如果后面的数字比前面的某个数字大,那么后面这个数字肯定不能作为左端点。所以先往单调栈里面加入一个递减序列。只有这个序列里的数字可以作为左端点。然后倒序枚举右端点。当右端点比单调栈里的数字大时,就弹出栈顶,不能继续弹出时统计答案。

/*
* @Author: wxyww
* @Date:   2019-01-19 21:29:18
* @Last Modified time: 2019-01-19 21:53:01
*/
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 1000000 + 100;
ll read() {
   ll x=0,f=1;char c=getchar();
   while(c<'0'||c>'9') {
      if(c=='-') f=-1;
      c=getchar();
   }
   while(c>='0'&&c<='9') {
      x=x*10+c-'0';
      c=getchar();
   }
   return x*f;
}
ll a[N],sum[N];
int sta[N],top;
int main() {
   int n = read(),m = read();
   for(int i = 1;i <= n;++i) a[i] = read();
   while(m--) {
      ll k = read();
      int ans = 0;
      top = 0;
      for(int i = 1;i <= n;++i) {
        sum[i] = sum[i - 1] + a[i] - k;
        if(sum[i] < sum[sta[top]]) {
            // printf("%lld ",sum[i]);
            sta[++top] = i;
        }
        // printf("%d ",sum[i]);
    }
    // printf("top:%d\n",top);
    sta[top + 1] = N;
    for(int i = n;i >= 1; --i) {
        while(sum[i] >= sum[sta[top-1]] && top) top--;
        ans = max(ans,i - sta[top]);
    }
    printf("%d\n",ans);
   }
   return 0;
}
/*
5 6

1 2 1 1 5

1 2 3 4 5 6
*/

你可能感兴趣的:(bzoj2086 Blocks)