cf1420 C2. Pokémon Army (hard version)

题意:

给定数组,你可以任选一个子序列 \(a_{b_1},\cdots ,a_{b_k}\),求它的交错和 $sum=a_{b_1}-a_{b_2}+a_{b_3}\cdots $

(注意不能改变顺序)

每次询问交换两个数,输出交换前后最大的子序列交错和

\(n,q\le 3e5, 1\le a_i\le n\)

思路:

法一:动态dp(线段树加速矩阵dp)

很容易写出dp:

\(f(i,1/0)\) 表示选的最后一个数的符号是正/负的,

初值 \(f(0,0)=f(0,1)=0\)

转移方程:

\(f(i,0)=\max \{f(i-1,0),f(i-1,1)-a_i \}\)

\(f(i,1)=\max \{f(i-1,1),f(i-1,0)+a_i \}\)

把乘法改成加法,加法改成 \(\max\),写出矩阵形式:

\[\begin{bmatrix} 0 & -a_i \\ a_i & 0 \end{bmatrix} \begin{bmatrix} f(i-1,0)\\f(i-1,1) \end{bmatrix} = \begin{bmatrix} f(i,0)\\f(i,1) \end{bmatrix} \]

用线段树维护一下(单点修改、查询全区间)

因为初始矩阵是全0的,我又把列向量看成2×2矩阵,所以矩阵是对称的,查询答案随便取矩阵中的两个不是对角的元素取 \(\max\) 即可

甚至懒得写build,建树的时候直接更新 n 次

const signed N = 3 + 3e5;
int n, q, a[N];

#define Matrix array<array<ll,2>,2>
Matrix mul(Matrix &a, Matrix &b) {
    Matrix res = {0,-LLINF,-LLINF,0}; //幺元
    for(int i = 0; i < 2; i++)
    for(int j = 0; j < 2; j++)
        for(int k = 0; k < 2; k++)
            res[i][j] = max(res[i][j], a[i][k] + b[k][j]);
    return res;
}

Matrix tr[N*4];
void upd(int u, int l, int r, int p, int x) {
    if(l == r) return tr[u] = {0,-x,x,0}, void();
    if(p <= mid) upd(ls, p, x); else upd(rs, p, x);
    tr[u] = mul(tr[lson], tr[rson]); //pushup
}
ll ans() {
    return max(tr[1][0][0], tr[1][1][0]);
}

void sol() {
    cin >> n >> q;
    for(int i = 1; i <= n; i++)
        cin >> a[i], upd(1, 1, n, i, a[i]);
    cout << ans() << endl;
    while(q--) {
        int x, y; cin >> x >> y;
        swap(a[x], a[y]);
        upd(1, 1, n, x, a[x]), upd(1, 1, n, y, a[y]);
        cout << ans() << endl;
    }
}

法二:

实际上要选所有波峰和波谷,当然开头和结尾会稍有不同

答案就是 \(\sum\limits_i \max \{ 0,a_i-a_{i-1} \}\)

每次询问只会改变几个点。注意交换相邻点的时候的操作有所不同

int n, q, a[N];
ll d(int i) { return max(0, a[i]-a[i-1]); }
void sol() {
    cin >> n >> q;
    a[0] = a[n+1] = 0; ll ans = 0;
    for(int i = 1; i <= n; i++)
        cin >> a[i], ans += d(i);
    cout << ans << endl;
    while(q--) {
        int x, y; cin >> x >> y;
        if(x + 1 == y) {
            ans -= d(x) + d(y+1) + d(y);
            swap(a[x], a[y]);
            ans += d(x) + d(y+1) + d(y);
        }
        else {
            ans -= d(x) + d(x+1) + d(y) + d(y+1);
            swap(a[x], a[y]);
            ans += d(x) + d(x+1) + d(y) + d(y+1);
        }
        cout << ans << endl;
    }
}
posted @ 2022-05-24 17:16  Bellala  阅读(60)  评论(0)    收藏  举报