[POI2010]Blocks

题目大意:
  给你一个长度为n的数列,给你m个数k。
  对于每个k,你可以进行若干次操作,每次把一个超过k的数的多余部分移到旁边一个数。
  问对于每个k,进行若干次操作以后,最长的满足每个数都不小于k的区间长度。

思路:
  一个区间可以通过若干次操作使得每个数都不小于k,当且仅当这个区间平均数大于等于k。
  对于每个k,我们可以先O(n)求出这个序列每个数减去k的前缀和。
  对于i>j,如果sum[i]>=sum[j],那么对于i之后的数,用i转移肯定比j更优。
  因此我们可以维护一个关于前缀和的单调递减栈。
  然后枚举区间的右端点,在单调栈中找到最左的满足前缀和之差大于等于0的左端点即可。

 1 #include<stack>
 2 #include<cstdio>
 3 #include<cctype>
 4 typedef long long int64;
 5 inline int getint() {
 6     register char ch;
 7     while(!isdigit(ch=getchar()));
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const int N=1000001;
13 int a[N];
14 int64 sum[N];
15 std::stack<int> s;
16 int main() {
17     const int n=getint(),m=getint();
18     for(register int i=1;i<=n;i++) {
19         a[i]=getint();
20     }
21     for(register int i=1;i<=m;i++) {
22         const int k=getint();
23         s.push(0);
24         for(register int i=1;i<=n;i++) {
25             sum[i]=sum[i-1]+a[i]-k;
26             if(sum[i]<sum[s.top()]) s.push(i);
27         }
28         int ans=0;
29         for(register int i=n;i;i--) {
30             int j=i;
31             while(!s.empty()&&sum[i]>=sum[s.top()]) {
32                 j=s.top();
33                 s.pop();
34             }
35             ans=std::max(ans,i-j);
36         }
37         printf("%d%c",ans," 
"[i==m]);
38         while(!s.empty()) s.pop();
39     }
40     return 0;
41 }
原文地址:https://www.cnblogs.com/skylee03/p/8183776.html