唔。挺简单的一道题但是很重要。。也花了很多时间。。
题目:
给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0。
请找到最长的一段连续区间,使得该区间内所有数字之和不超过p
分析:
看道题的时候有点想法。
想了想感觉像是单调队列。就这么顺着往下走。。
发现肯定能选d个数该最好哈,所以统计每个点起始的可以修改的值是多少也就是f[i]=sum[i+d-1]-sum[i-1]对吧!
所已我们关心的结果就变成了 sum[i]-sum[j-1]-f_max<=p的最大的i-j+1.
所以 我们枚举右端点,维护一个下标对应的f单调递减的序列
O(2n)处理。
纠结如此之久是因为,不会写 单调队列了。好怕qaq。伦家不想afo。
但是交题的AC率啊!!!
因为WA 还写了对拍。最大的数据硬是没拍出来错。
后来果断发现自己算f时候的循环写少了。
还TLE对比下面的515221650的队短,我写的常数那么大吗?!
事实证明快读还是很有用的,毕竟一半时间呢,,,
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
//by mars_ch
using namespace std;
int n,p,d,ans;
int sum[2000005];
int f[2000005];
int q[2000005];
int read()
{
int s=0ll;
char p=getchar();
while(p<'0' ||p>'9')p=getchar();
while(p>='0' && p<='9') s=s*10+p-'0',p=getchar();
return s;
}
signed main()
{
n=read(),p=read(),d=read();
for(int i=1;i<=n;i++)
{
int a;
a=read();
sum[i]=sum[i-1]+a;
}
for(int i=1;i<=n-d+1;i++)
{
f[i]=sum[i+d-1]-sum[i-1];
}
int h=1,t=1,k=1;
for(int i=d;i<=n;i++) //枚举右端点
{
while(h<=t && f[i-d+1]>=f[q[t]])
{
t--;
}
q[++t]=i-d+1;
while(sum[i]-sum[k-1]-f[q[h]]>p)
{
k++;
while(h<t && q[h]<k) h++;
}
if(q[h]>=k) ans=max(ans,i-k+1);
}
printf("%lld\n",ans);
return 0;
}