题目大意:给你n个数,要你求一段长度不超过k的、和最大的连续子序列,问你最大的和是多少。
解题思路:单调队列+前缀和优化。
可以发现,i到j的和与k到j的和,当j变化时,这两个和的相对大小关系是不变的。
我们用单调队列保存与当前第i个位置距离小于k(等于则不能加到)的位置,并让这个位置加到i的和单调递减。
这样就能保证每次从队头弹出时,之后的和仍是最大。
求一段连续序列的和就用到前缀和优化。
总时间复杂度$O(n)$。
C++ Code:
#include<cstdio>
#include<cctype>
#define N 500005
int n,k,a[N],sa[N],q[N<<1],h,t;
inline int max(int a,int b){return a<b?b:a;}
inline int readint(){
char c=getchar();
bool b=false;
for(;!isdigit(c);c=getchar())b=c=='-';
int d=0;
for(;isdigit(c);c=getchar())
d=(d<<3)+(d<<1)+(c^'0');
return b?-d:d;
}
int main(){
n=readint(),k=readint();
sa[0]=0;
for(int i=1;i<=n;++i)sa[i]=sa[i-1]+(a[i]=readint());
int ans=max(0,a[1]);
if(k==1){
for(int i=1;i<=n;++i)ans=max(ans,a[i]);
printf("%d\n",ans);
return 0;
}
q[1]=h=t=1;
for(int i=2;i<=n;++i){
while(q[h]+k<=i)++h;
while(h<=t&&a[i]>sa[i]-sa[q[t]-1])--t;
q[++t]=i;
ans=max(ans,sa[i]-sa[q[h]-1]);
}
printf("%d\n",ans);
return 0;
}