题目大意:给出N个正整数a[1..N],再给出一个正整数k,现在可以进行如下操作:每次选择一个大于k的正整数a[i],将a[i]减去1,选择a[i-1]或a[i+1]中的一个加上1。经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数都不小于k。M组询问
这个询问条件可以转化为询问最长的平均数大于等于K的区间
对于每组询问我们分别计算,先给每个数全都减去K,这样就相当于问平均数大于等于0的最长区间,也就是区间和大于等于0的最长区间
我们做一个前缀和,相当于求max(i-j),pre[i]>=pre[j]
这一步怎么求呢?我们可以先从左到右扫一遍,每当当前点的值小于栈顶时,就把它压入栈顶,这样扫完之后在栈里的元素就是所有可能的左端点
然后开始枚举右端点,一直弹栈直到栈顶比他大,弹出去的最后一个元素就是以他为右端点最靠左的左端点
这样为什么一定能更新出答案呢?因为当你求出一个值之后,右端点会继续向左移动,所以左端点一定也要向左移动才可能更新出更优的解,所以之前弹出去的那些元素就已经没用了,弹出去就好
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000010
using namespace std;
long long a[N],b[N],pre[N];
long long s[N],t;
long long n,m;
bool F;
void solve()
{
long long i,j,x,y;
t=1;s[1]=0;
for(i=1;i<=n;i++)
if(pre[s[t]]>pre[i])
{
t++;
s[t]=i;
}
long long ans=0;
s[t+1]=n;
for(i=n;i>=1;i--)
{
while(t&&pre[s[t]]<=pre[i]) t--;
ans=max(ans,i-s[t+1]);
}
printf("%lld",ans);
if(!F) printf(" ");
}
int main()
{
scanf("%lld%lld",&n,&m);
long long i,j,x,y;
for(i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(i=1;i<=m;i++)
{
scanf("%lld",&x);
for(j=1;j<=n;j++)
{
b[j]=a[j]-x;
pre[j]=pre[j-1]+b[j];
}
if(i==m) F=true;
solve();
}
}