[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," \n"[i==m]); 38 while(!s.empty()) s.pop(); 39 } 40 return 0; 41 }