题意:
给出N个正整数a[1..N],再给出一个正整数k,现在可以进行如下操作:每次选择一个大于k的正整数a[i],将a[i]减去1,选择a[i-1]或a[i+1]中的一个加上1。经过一定次数的操作后,问最大能够选出多长的一个连续子序列,使得这个子序列的每个数都不小于k。
总共给出M次询问,每次询问给出的k不同,你需要分别回答。
第一行两个正整数N (N <= 1,000,000)和M (M<=50) 。
第二行N个正整数,第i个正整数表示a[i] (a[i]<=109) 。
第三行M个正整数,第i个正整数表示第i次询问的k (k<=109) 。
解析:
解决办法倒是很好想,不过怎么实现?
办法其实就是把每一个数与k作差然后求一个前缀和,之后我们要求max(j-i)并且sum[j]-sum[i]>=0
初步思路就是搞一个单调数据结构来解这个问题。
后来单调栈写的我都蛋疼了,一定是做法出了问题。
我们首先明确,如果我们倒着找每一个数最左边的点在哪的话,那么对于答案来说,左边的点显然是单调的。
所以我们可以考虑首先搞出来一个sum的单调递减序列。
之后倒着枚举整个sum,从这个单调递减序列中找到对于答案有贡献的点最左边能到哪即可。不断弹栈即可完成这一过程。
代码:
#include
#include
#include
#include
#define N 1000100
using namespace std;
typedef long long ll;
int n,m;
ll a[N];
ll sum[N];
int top;
int sta[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=m;i++)
{
ll qx;
scanf("%lld",&qx);
for(int j=1;j<=n;j++)sum[j]=sum[j-1]+a[j]-qx;
top=0;
sta[++top]=0;
for(int j=1;j<=n;j++)
if(sum[j]int ans=0;
for(int j=n;j>=0;j--)
{
while(top>0&&sum[j]>=sum[sta[top]])top--;
ans=max(ans,j-sta[top+1]);
}
if(i==m)printf("%d\n",ans);
else printf("%d ",ans);
}
}