[CF] C. Remove the Ends - Codeforces Round 1005 (Div. 2)

https://codeforces.com/contest/2064/problem/C

题目大意

给出 $n$ 长的不含 $0$ 的数组 $a$。每次可以选择当前一个正数 $a_i$ 将其加到答案,并将数组的 $i$ 结束的前缀删掉,或者选择某个负数 $a_i$ 将 $- a_i$ 加到答案,并将 $i$ 开始的后缀删掉。问可以得到的最大答案是多少。

$1 \le n \le 2 \times 10^5$。$- 10^9 \le a_i \le 10^9, a_i \neq 0$。

简要题解

观察:

  1. 总是尽量先取最右边的负数或者最左边的正数,否则我们会比这样取删掉更多数。
  2. 取完最边上的数之后,总是把其旁边的所有和它符号相同的数取完。假设先取了左边的正数,则显然下次取左侧还会取这里,而取右侧负数也不会删掉这个数。因此我们可以将所有的符号相同的数压缩在一起变成数组 $b$。
  3. 所以我们其实就是要处理一个 -+-+-+ 这样的序列的最优值。

对于 3 由于我们每次都是删除边上的一个,容易想到这是一个区间 dp 的转移

$$ dp[l][r] = max(dp[l + 2][r] + b[l + 1], dp[l][r - 2] + b[r - 1]) $$

但显然这是 $O(n ^ 2)$ 的复杂度无法通过本题。这里我们注意到,如果最后处理的区间位置确定,我们就知道其左右各做了多少次区间收缩,而无论收缩顺序如何,其权值是确定的,因此我们只要枚举左右各进行 $i$ 次和 $\frac{|b|}{2} - i$ 次收缩即可。

复杂度

$T$:$O(n)$

$S$:$O(n)$

代码实现

#include <bits/stdc++.h>
using namespace std;

int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();

using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;

template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) { 
    return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) { 
    return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z 
    if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
    if(x<z) { y=x; x=z; } else if(y<z) y=z; }

// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());

/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/

/*
+ + - - - + + + - - - + - -
    - - - + + + - - - +

*/

inline int sgn(LL x) { return (x > 0) - (x < 0); }

void solve() {
  int n; cin >> n;
  vector<int> a(n);
  for (int& i : a) cin >> i;

  vector<LL> b;
  for (int i = 0; i < n; ) {
    int j = i;
    LL cur = 0;
    while (j < n && sgn(a[j]) == sgn(a[i])) {
      cur += a[j];
      j++;
    }

    b.push_back(cur);
    i = j;
  }

  int l = 0, r = b.size() - 1;
  LL ans = 0;
  if (b[l] > 0) {
    ans += b[l];
    l++;
  }
  if (l <= r && b[r] < 0) {
    ans += -b[r];
    r--;
  }

  if (l <= r) {
    int len = (r - l + 1) / 2;
    vector<LL> c;
    vector<LL> d;
    for (int i = l; i <= r; i += 2) {
      c.push_back(b[i + 1]);
      d.push_back(-b[i]);
    }
    for (int i = 1; i < len; i++) {
      c[i] += c[i - 1];
    }
    for (int i = len - 2; i >= 0; i--) {
      d[i] += d[i + 1];
    }
    LL mx = max(c[len - 1], d[0]);
    for (int i = 0; i + 1 < len; i++) {
      cmax(mx, c[i] + d[i + 1]);
    }

    ans += mx;
  }

  cout << ans << '\n';
}

int main() {
  int t = 1; 
  cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}
Prev: [CF] D. Eating - Codeforces Round 1005 (Div. 2)
Next: [CF] B. Variety is Discouraged - Codeforces Round 1005 (Div. 2)