[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 }

 

posted @ 2018-08-08 19:48  Xu-daxia  阅读(239)  评论(0编辑  收藏  举报