一道关于单调队列的模板题。
题目要求求一段区间,使得这一段区间的和最大且区间长度不超过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 ",ans); 32 return 0; 33 }