题意
思路
我们发现,如果从序列中某个元素A(i)开始向右构造子序列,其能组成的子序列为A(i),A(i)+A(i+1),……,A(i)+……+A(i+m-1),如果我们需要求从Ai开始的长度不超过m的各个非空子序列的值,每次都累加显然会有不小的时间负担,因此我们会考虑到使用前缀和,这时以A(i)+……+A(i+m-1)为例,其值就可以表示为前缀和(i+m-1)- 前缀和(i-1)。
接下来我们要求从A(i)开始向右构造子序列的所有子序列中的最大值,那我们就会发现,对于这些子序列,其实减数【前缀和(i-1)】是不变的,变的只是被减数【前缀和(i),前缀和(i+1),……,前缀和(i+m-1)】,求序列最大值,就可以转化为求被减数的最大值,而被减数的最大值,就是一个不超过m的区间里的前缀和的最大值,既然如此,我们就能想到滑动窗口,用单调队列来处理前缀和数组。
最后,只要比较以A(1),A(2),……,A(m)为开头的向右构造长度不超过m的子序列的最大值,找出他们的最大值,就是答案了。
代码
#include <iostream>
#include <queue>
using namespace std;
typedef struct a{
int index,num;
}a;
const int maxn=2e5+5;
a arr[maxn];
int main(){
deque<a>window;
int n,m;
cin>>n>>m;
arr[0].num=0;
for(int i=1;i<=n;i++){
cin>>arr[i].num;
arr[i].num+=arr[i-1].num;
arr[i].index=i;
}
int ans=arr[1].num;
for(int i=1;i<=n;i++){
while(!window.empty()&&window.back().num<arr[i].num)window.pop_back();
if(!window.empty()&&window.front().index<=i-m)window.pop_front();
window.push_back(arr[i]);
if(i-m>0){
ans=max(ans,window.front().num-arr[i-m].num);
}
else{
ans=max(ans,window.front().num-arr[0].num);
}
}
for(int i=n-m+1;i<n;i++){
while(!window.empty()&&window.front().index<=i)window.pop_front();
ans=max(ans,window.front().num-arr[i].num);
}
cout<<ans;
}