版权声明:未经本蒟蒻同意,请勿转载本蒻博客 https://blog.csdn.net/wddwjlss/article/details/83421191
题意:给定一个长度为 的序列,你有一次机会选中一段连续的长度不超过 的区间,将里面所有数字全部修改为 。请找到最长的一段连续区间,使得该区间内所有数字之和不超过 。
首先选长度为 的区间一定优于选长度小于 的区间,而这种固定区间的大概需要 复杂度的 问题,很容易想到用单调队列优化。
首先先是朴素的 ,我们用双指针, 表示选择的区间的右端点, 表示以 为右端点的区间保证数字之和 的最靠左的那个数的位置,随着 的增大, 一定单调不降。
但是现在可以将一段区间变为 ,所以单调队列维护的就是 到 这个区间内所有长度为 的连续区间的和的最大值的区间右端点。首先求一个前缀和,并用 数组记录序列中所有长度为 的区间的和。
从 个位置开始枚举,每次将 放入队列中,踢掉在 之前且比 小的,如果队首元素的左端点比 还小,则将队首元素剔除。对于 指针的更新我们需要判断 到 这段区间的和减去这个区间的最大值是否大于 ,对于每一次 指针的更新,都要看队首元素的左端点和 的大小关系,踢掉无用的队头元素。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,dp[2001000],d,a[2001000],ans,q[2001000];
long long p,sum[2001000],b[2001000];
int main()
{
scanf("%d%lld%d",&n,&p,&d);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=d;i<=n;++i)
b[i]=sum[i]-sum[i-d];
ans=d;
int head=1,tail=0,last=1;
for(int i=d+1;i<=n;++i)
{
while(head<=tail&&b[i]>b[q[tail]])
tail--;
q[++tail]=i;
while(head<=tail&&q[head]-d+1<last)
head++;//如果队首元素的左端点比last还小
while(head<=tail&&sum[i]-sum[last-1]-b[q[head]]>p)
{
last++;
while(head<=tail&&q[head]-d+1<last)
head++;
}
ans=max(ans,i-last+1);
}
cout<<ans;
return 0;
}