思路
- 计算区间和问题一般转化成前缀和问题,所以原问题可以转化成找出两个位置 l l l 与 r r r 使得 a [ r ] − a [ l − 1 ] a[r]-a[l-1] a[r]−a[l−1]最大并且 r − l + 1 < = m r-l+1 <= m r−l+1<=m。
- 首先我们枚举 i i i,当 i i i固定为右端点时,问题就转化成找一个左端点 j j j, j j j 范围为 [ i − m + 1 , i − 1 ] [i-m+1,i-1] [i−m+1,i−1],并且 a [ j − 1 ] a[j-1] a[j−1] 最小(因为要使得 i i i 到 j j j 的和 a [ i ] − a [ j − 1 ] a[i]-a[j-1] a[i]−a[j−1]最大)。
- 这时就容易想到单调队列( q u e que que 为下标的队列):当队尾的值 a [ q u e [ t − 1 ] ] a[que[t-1]] a[que[t−1]] 大于等于 a [ i ] a[i] a[i] 时完全可以把队尾弹出,因为i下标更大(更不容易被淘汰)且值更小。那么队头的值就会一定是最小的,这里要注意的是,右端点更新时要判断一下是否弹出队头(保持 i − q u e [ t − 1 ] + 1 < = m i-que[t-1]+1 <= m i−que[t−1]+1<=m )。
#include<iostream>
#define int long long
using namespace std;
const int N = 300010;
int a[N],que[N],h,t;
signed main(){
int n,m; cin>>n>>m;
for(int i = 1; i <= n; i++) {
int x; cin>>x;
a[i] = a[i-1] + x;
}
int res = -0x3f3f3f3f;
h = 0,t = 1;
for(int i = 1; i <= n; i++){
if(h < t && i - que[h] > m) h++;
res = max(res, a[i] - a[que[h]]);
while(h < t && a[que[t-1]] >= a[i]) t--;
que[t++] = i;
}
cout<<res;
return 0;
}
参考
https://www.acwing.com/solution/content/28015/