链接
https://www.luogu.org/problemnew/show/P1714
题意
给你一个长度为 n n n的整数序列,需要你求出区间长度不超过 k k k的最大区间和;
分析
首先得到前缀和 s u m sum sum,对于最大区间和,我们即要求 max ( s u m [ i ] − s u m [ j ] ) ( i − j < = k ) \max(sum[i]-sum[j])(i-j<=k) max(sum[i]−sum[j])(i−j<=k),这里可以用线段树、树状数组求,对于每个 i i i来说,我们只需要求最小的 s u m [ j ] sum[j] sum[j],其中 j ∈ [ i − k , i ] j\in[i-k,i] j∈[i−k,i],即转换为一个求区间最小值问题;
但是这里有一种 O ( n ) O(n) O(n)的方法,使用单调队列维护递增的长度为 k k k的 s u m sum sum,对于当前入队的 s u m sum sum来说,在它入队之前的所有状态的最值已经求出来了,现在我们可以利用它来继续更新,让队尾大于它的元素出队,并且不满足区间长度 k k k的从队首出队;当前 s u m sum sum入队之后,我们就用队首 s u m sum sum减队尾 s u m sum sum(因为队列 s u m sum sum递增),来更新我们的最大区间和;队列元素为下标,另外 s u m [ 0 ] sum[0] sum[0]也要考虑;
代码
#include <functional>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>
#define INF 0x7f7f7f7f
#define MAXN 1000005
#define N 200005
#define P 2
#define MOD 99991
typedef long long ll;
namespace fastIO {
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1<<22, stdin), p1 == p2) ? EOF : *p1++)
//char buf[(1 << 22)], *p1 = buf, *p2 = buf;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
}
using namespace fastIO;
using namespace std;
int n, k, sum[MAXN];
int q[MAXN], h, t;
int main() {
cin >> n >> k;
int x;
for (int i = 1; i <= n; i++)
cin >> x, sum[i] += sum[i - 1] + x;
h = 1;
int res = 0;
for (int i = 0; i <= n; i++) {
while (h <= t && i - q[h] > k)h++;
while (h <= t && sum[q[t]] >= sum[i])t--;
q[++t] = i;
res = max(res, sum[i] - sum[q[h]]);
}
cout << res << endl;
}