loj 最大连续和

loj

简化题意

给你 n 个数的序列,找出一段连续的长度不超过 m 的非空子序列,使得这个序列的和最大。
\(1 \leq n , m \leq 2e5\)


思路

有一个非常显然的 DP,\(F_i = \max \Big\{ \sum_{j = i - k + 1}^i A_j \big| k \in [1,m]\Big\}\)

然而这样做是 \(\operatorname{O}(nm)\) 的,并不优秀。

我们令 \(s_i = \sum_{j = 1}^i A_j\),则:

\[\begin{aligned} F_i&=\max \Big\{ \sum_{j = i - k + 1}^i A_j \big| k \in [1,m] \Big\} \\ F_i&=\max \Big\{ s_i - s_{i - k} \big| k \in [1, m]\Big\}\\ F_i&=s_i - \min\Big\{ s_{i-k} \big| k\in[1,m]\Big\} \end{aligned} \]

考虑用队列来维护决策值 \(s_{i-k}\) ,每次只需要在队首删除 \(s_{i-k-1}\),在队尾加入 \(s_{i-1}\),但是取最小值的操作还是需要 \(\operatorname{O}(m)\) 实现。

考虑在添加 \(s_{i-1}\) 的时候,设现在队尾元素为 \(s_k\) ,由于 \(k < i-1\),所以 \(s_k\) 必然比 \(s_{i-1}\) 先出队。若此时 \(s_{i-1} \leq s_k\),则 \(s_k\) 这个决策就永远不会再以后用到,可以将 \(s_k\) 从队尾删除,此时的队尾就形成了一个类似栈的一个东西。

同理,若队尾的两个元素 \(s_i\)\(s_j\) ,若 \(i < j\)\(s_i \geq s_j\),则我们可以删掉 \(s_i\),因为 \(s_i\) 永远都不会用到了,此时的队列中的元素构成了一个单调递增的序列,即 \(s_1 < s_2 < \dots < s_k\)

维护的时候只需要这么做就行了。

  • 若当前队首元素 \(s_x\),有 \(x < i - m\) , 则 \(s_x\) 出队,知道队首元素 \(x \geq i-m\) 为止。
  • 若当前队尾元素 \(s_k \geq s_{i-1}\),则 \(s_k\) 出队,知道 \(s_k < s_{i-1}\) 为止。
  • 在队尾插入 \(s_{i-1}\)
  • 取出队列中的最小值,更新一下答案。

代码

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define N 200010
#define M 1010

using namespace std;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
int n, m, head, tail;
int s[N], a[N], q[N], f[N], num[N];

int read() {
  int s = 0, f = 0; char ch = getchar();
  while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
  while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
  return f ? -s : s;
}

int main() {
  n = read(), m = read();
  for (int i = 1; i <= n; i++) a[i] = read(), s[i] = s[i - 1] + a[i];
  int ans = -inf;
  tail = 1, head = 1, q[tail] = 0, num[tail] = 0;
  for (int i = 1; i <= n; i++) {
    while (num[head] < i - m && head <= tail) head++;
    ans = max(ans, s[i] - q[head]);
    while (q[tail] >= s[i] && head <= tail) tail--;
    q[++tail] = s[i], num[tail] = i;
  }
  cout << ans;
}
posted @ 2020-11-12 08:29  Kersen  阅读(167)  评论(1)    收藏  举报