【CH1201】最大子序列和

一道关于单调队列的模板题。

题目要求求一段区间,使得这一段区间的和最大且区间长度不超过m。我们显然想到了先求出这个序列的前缀和sum,这样我们就能用O(1)的时间查询任意一个子序列的和。

现在,我们枚举区间的右端点,对于每一个右端点i,我们要找到一个左端点j,使得sum[j]最小而且i-j≤m.

因此,我们维护一个单调队列q。队列中存储左端点的下标,按照此下标所对应的sum值单调递增。当我们枚举到i时,我们检查队头的元素(最靠左的下标)是否满足题意(是否与i不超过m),将非法的下标出队,对于当前的i,j就是队头的元素。我们更新一次答案。之后,我们删除队尾,直到队尾对应的sum值小于sum[i],然后将i入队。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 typedef long long ll;
 7 int n,m,a[300010],sum[300010];
 8 inline int read() {
 9     int ret=0;
10     int op=1;
11     char c=getchar();
12     while(c<'0'||c>'9') {if(c=='-') op=-1; c=getchar();}
13     while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
14     return ret*op;
15 }
16 int ans,q[300010],l,r;
17 int main() {
18     n=read(); m=read();
19     for(int i=1;i<=n;i++) {
20         a[i]=read();
21         sum[i]=sum[i-1]+a[i];
22     }
23     l=r=1;
24     q[1]=0;
25     for(int i=1;i<=n;i++) {
26         while(l<=r&&i-m>q[l]) l++;
27         ans=max(ans,sum[i]-sum[q[l]]);
28         while(l<=r&&sum[i]<=sum[q[r]]) r--;
29         q[++r]=i;
30     }
31     printf("%d\n",ans);
32     return 0;
33 }
AC Code

 

posted @ 2019-05-18 09:42  AD_shl  阅读(210)  评论(0编辑  收藏  举报