[POI2015]WIL-Wilcze doły(单调队列)
题意
给定一个长度为n的序列,你有一次机会选中一段连续的长度不超过d的区间,将里面所有数字全部修改为0。请找到最长的一段连续区间,使得该区间内所有数字之和不超过p。
(1<=d<=n<=2000000,0<=p<=10^16)
题解
一看以为是DP结果想了半天想不出来。(其实有点像)
然后又以为是单调队列套单调队列。。。(我也不知到这是什么)
但跟答案很像了,就差一点。
假如给定选择区间,一定要去掉和最大的连续d个数。
所以我们枚举以右端点r,用单调对列维护当前区间内最大的连续d个数的值。
在区间扩展时(r++)我们判断当前区间在去掉最大的连续d个数的值之后是否大于p
若大于p则l++更新单调队列。对于每一个右端点都有一个最优解。取最大就好了。
o(n)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const long long N=2000100; 8 long long n,p,d,a[N],sum1[N],sum[N],q[N],l,head,tail,tot,ans; 9 int main(){ 10 scanf("%lld%lld%lld",&n,&p,&d); 11 for(long long i=1;i<=n;i++){ 12 scanf("%lld",&a[i]); 13 sum1[i]=sum1[i-1]+a[i]; 14 } 15 for(long long i=1;i<=d;i++){ 16 sum[i]=sum1[i]; 17 } 18 for(long long i=d+1;i<=n;i++){ 19 sum[i]=sum1[i]-sum1[i-d]; 20 } 21 // for(int i=1;i<=n;i++){ 22 // cout<<sum[i]<<" "; 23 // } 24 // cout<<endl; 25 head=1; 26 tail=0; 27 l=1; 28 for(long long i=1;i<=n;i++){ 29 tot+=a[i]; 30 while(sum[i]>=sum[q[tail]]&&head<=tail)tail--; 31 q[++tail]=i; 32 while(tot-sum[q[head]]>p&&head<=tail){ 33 tot-=a[l]; 34 l++; 35 while(q[head]-d<l&&head<=tail)head++; 36 } 37 ans=max(ans,i-l+1); 38 } 39 printf("%lld",ans); 40 return 0; 41 }