题目大意:
给你一个长度为n的数列,给你m个数k。 对于每个k,你可以进行若干次操作,每次把一个超过k的数的多余部分移到旁边一个数。 问对于每个k,进行若干次操作以后,最长的满足每个数都不小于k的区间长度。思路:
一个区间可以通过若干次操作使得每个数都不小于k,当且仅当这个区间平均数大于等于k。 对于每个k,我们可以先O(n)求出这个序列每个数减去k的前缀和。 对于i>j,如果sum[i]>=sum[j],那么对于i之后的数,用i转移肯定比j更优。 因此我们可以维护一个关于前缀和的单调递减栈。 然后枚举区间的右端点,在单调栈中找到最左的满足前缀和之差大于等于0的左端点即可。1 #include2 #include 3 #include 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 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()]) {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 }