题意

个数,每个数取值范围为 。现在要在两两数之间插入 两种运算符,使得运算结果最大化,求最大化的结果。
其中,

分析

这里提供一种非主流做法。
最终的形式肯定是一块块乘积相加的形式,于是我们考虑
表示来到 时的最大值。
那么 。其中, 指的是 这段区间每个数的乘积。
由于 的取值只有 三种情况,我们分情况考虑:

  1. 乘积为 ,也就是这段区间中包含了至少 ,那这段区间一定包含了离 最近的 ,不妨记这个位置为 ,那么
  2. 乘积不为 ,那么决策点一定是在 中进行选择。
    如果 ,那么最优的方式肯定是从 转移过来,即
    如果 ,那么可以有两种转移方式:
    第一种是 。也就是直接从 转移过来。
    第二种是找到上一个 的位置 ,那么 这段区间的乘积为

可以发现上面的转移是可以保证答案最优的。
由于 是递增的,复杂度可以做到 。(当然用线段树也可以)

代码如下

#include <bits/stdc++.h>
using namespace std;
int read(){
    int x, f = 1;
    char ch;
    while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
    x = ch - '0';
    while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
    return x * f;
}
const int N = 100005;
int a[N], f[N], val;
int main(){
    int i, j, n, m, T, p, las;
    T = read();
    while(T--){
        n = read();
        for(i = 1; i <= n; i++) f[i] = 0, a[i] = read();
        p = las = val = 0;
        for(i = 1; i <= n; i++){
            if(a[i] == 0){
                while(p < i) val = max(val, f[++p]);
            }
            f[i] = f[i - 1] + a[i];
            if(p > 0) f[i] = max(f[i], val);
            if(a[i] == -1){
                if(las > p) f[i] = max(f[i], f[las - 1] + 1);
                las = i;
            }
        }
        printf("%d\n", f[n]);
    }
    return 0;
}