luogu P4719 【模板】动态dp

版权声明:_ https://blog.csdn.net/lunch__/article/details/84728818

题意

  • 给定一棵 n n 个点的树,点带点权。有 m m 次操作,每次操作给定 x , y x,y ,表示修改点 x x 的权值为 y y ,你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

为了做 N O I P D 2 T 3 NOIP D2T3 学的这个鬼东西…

感觉理解起来有点点难,还是要多看几遍

首先我们这个题可以很简单写出一个方程

f [ x ] [ 0 ] f[x][0] 为不选 x x x x 子树内的最大独立集
f [ x ] [ 1 ] f[x][1] 为选 x x x x 子树内的最大独立集

f [ x ] [ 1 ] = v a l [ x ] + y s o n [ x ] f [ y ] [ 0 ] f[x][1]=val[x]+\sum_{y \in son[x]}f[y][0]
f [ x ] [ 0 ] = y s o n [ x ] m a x ( f [ y ] [ 0 ] , f [ y ] [ 1 ] ) f[x][0] = \sum_{y\in son[x]}max(f[y][0], f[y][1])

我们转移到某一个儿子 y y 的时候

那么有式子
f [ x ] [ 1 ] = S 1 + f [ y ] [ 0 ] f[x][1]=S1+f[y][0]
f [ x ] [ 0 ] = S 0 + m a x ( f [ y ] [ 0 ] , f [ y ] [ 1 ] ) f[x][0]=S0+max(f[y][0], f[y][1])

S 0 , S 1 S0,S1 代表剩余的其他儿子的 d p dp 值之和

显然 S 0 S 1 S0,S1 的计算与 y y 无关

我们重新定义矩阵乘法 C = A B C=A*B

C [ i ] [ j ] = m a x k = 1 K ( A [ i ] [ k ] + B [ k ] [ j ] ) C[i][j]=max_{k=1}^K(A[i][k]+B[k][j])

再把转移写成矩阵的形式
( f [ x ] [ 0 ] , f [ x ] [ 1 ] ) = ( f [ y ] [ 0 ] , f [ y ] [ 1 ] ) ( S 0 S 1 S 0 inf ) (f[x][0],f[x][1])=(f[y][0],f[y][1])* \left( \begin{matrix} S_0 & S_1 \\ S_0 & -\inf \end{matrix} \right)
那么我们就可以通过矩阵动态维护 d p dp

我们首先进行树链剖分

令每个点的矩阵为从重儿子转移到自身的矩阵

那么我们每当对一个点进行修改的时候

它的实父亲的转移矩阵是不会有变化的

但是每条轻边会导致一个点的转移矩阵变化

那么我们可以用线段树快速查询出一个点的 d p dp

再用它去更新它虚父亲的转移矩阵即可

其中改变是这样的

定义转移后的 d p dp 值为 F F

那么 S 0 + = m a x ( F [ y ] [ 1 ] , F [ y ] [ 0 ] ) m a x ( f [ y ] [ 1 ] , f [ y ] [ 0 ] ) S0+=max(F[y][1],F[y][0])-max(f[y][1],f[y][0])
S 1 + = F [ y ] [ 0 ] f [ y ] [ 0 ] S1+=F[y][0] -f[y][0]

总结一下就是先记下当前重链顶端的 d p dp

再修改当前点的转移矩阵

然后就是求出当前重链顶端的 d p dp

再更新与这条重链通过轻边相连的上一条重链

这样子复杂度是 O ( 2 3 n log 2 n ) O(2^3n\log^2n)

Codes

#include <bits/stdc++.h>

#define pb push_back
#define inf (0x3f3f3f3f)

using namespace std;

const int N = 1e5 + 10;

struct Martix {

    int a[2][2]; 

    Martix() {a[0][0] = a[0][1] = a[1][0] = a[1][1] = -inf;}

    Martix operator * (const Martix &T) const {
        Martix res; 
        res.a[0][0] = max(a[0][0] + T.a[0][0], a[0][1] + T.a[1][0]);
        res.a[0][1] = max(a[0][0] + T.a[0][1], a[0][1] + T.a[1][1]);
        res.a[1][0] = max(a[1][0] + T.a[0][0], a[1][1] + T.a[1][0]);
        res.a[1][1] = max(a[1][0] + T.a[0][1], a[1][1] + T.a[1][1]);
        return res;
    }

}g[N];

void P(Martix x) {
    cout << x.a[0][0] << ' ' << x.a[0][1] << endl << x.a[1][0] << ' ' << x.a[1][1] << endl;
}

vector<int> G[N];

int n, m, a[N], f[N][2]; 
int size[N], fa[N], heavy[N];
int top[N], st[N], ed[N], rel[N], cnt;

void dfs1(int x) {
    size[x] = 1, f[x][1] = a[x]; 
    for (auto v : G[x]) if (!size[v]) {
        fa[v] = x; 
        dfs1(v); 
        f[x][0] += max(f[v][0], f[v][1]);
        f[x][1] += f[v][0];
        size[x] += size[v];
        if (size[v] > size[heavy[x]])
            heavy[x] = v; 
    }
    int s0 = f[x][0] - max(f[heavy[x]][0], f[heavy[x]][1]), s1 = f[x][1] - f[heavy[x]][0];
    g[x].a[0][0] = g[x].a[1][0] = s0, g[x].a[0][1] = s1, g[x].a[1][1] = -inf;
}

void dfs2(int x, int ancestor) {
    rel[st[x] = ed[top[x] = ancestor] = ++ cnt] = x;
    if (heavy[x]) dfs2(heavy[x], ancestor);
    for (auto v : G[x]) if (!top[v])
        dfs2(v, v);
}

struct Segment_Tree {
#define mid ((l + r) >> 1)
#define ls (bh << 1)
#define rs (ls | 1)
#define lson ls, l, mid
#define rson rs, mid + 1, r

    Martix S[N << 2]; 

    void pushup(int bh) {
        S[bh] = S[rs] * S[ls];
    }

    void build(int bh, int l, int r) {
        if (l == r) S[bh] = g[rel[l]];
        else build(lson), build(rson), pushup(bh);
    }

    void update(int bh, int l, int r, int x) {
        if (l == r) S[bh] = g[rel[x]];
        else {
            if (x <= mid) update(lson, x);
            else update(rson, x);
            pushup(bh);
        }
    }

    Martix query(int bh, int l, int r, int x, int y) {
        if (x <= l && r <= y) return S[bh];
        if (x > mid) return query(rson, x, y);
        if (y <= mid) return query(lson, x, y);
        return query(rson, x, y) * query(lson, x, y);
    }
    
}T;

int modify(int x, int y) {
    Martix lst, now;
    while (x) {
        int del0, del1; 
        if (y) del1 = y, del0 = y = 0;
        else {
            del0 = max(now.a[0][0], now.a[0][1]) - max(lst.a[0][0], lst.a[0][1]);
            del1 = now.a[0][0] - lst.a[0][0];
        }
        lst = T.query(1, 1, n, st[top[x]], ed[top[x]]);
        g[x].a[0][0] += del0, g[x].a[1][0] += del0, g[x].a[0][1] += del1;
        T.update(1, 1, n, st[x]);
        now = T.query(1, 1, n, st[top[x]], ed[top[x]]);
        x = fa[top[x]];
    }
    return max(now.a[0][0], now.a[0][1]);
}

int main() {
    //freopen("ddp.in", "r", stdin);
    //freopen("ddp.out", "w", stdout);

    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++ i)
        scanf("%d", &a[i]);
    for (int x, y, i = 1; i < n; ++ i) {
        scanf("%d%d", &x, &y);
        G[x].pb(y), G[y].pb(x);
    }
    dfs1(1), dfs2(1, 1), T.build(1, 1, n);

    //P(T.query(1, 1, n, st[1], ed[1]));
    //cout << max(f[1][0], f[1][1]) << endl;

    for (int x, y; m -- ; ) {
        scanf("%d%d", &x, &y);
        if (y == a[x]) {
            Martix res = T.query(1, 1, n, st[1], ed[1]);
            printf("%d\n", max(res.a[0][0], res.a[0][1]));
        }
        else printf("%d\n", modify(x, y - a[x])), a[x] = y;
    }

    return 0;
}

最后感谢这位大佬博客的指导

猜你喜欢

转载自blog.csdn.net/lunch__/article/details/84728818