动态DP(ddp)

动态dp(ddp)

昨天模拟赛 T3 考了到 ddp 板子题,没认真学习的我丢掉了 60 分……

在这里就是讲讲 模板题

题面描述

求一个树的最大独立集,点带权,权值带修改。$n,q \leq 10^5$

题解

首先考虑没有带修改,或者是 $O(nq)$ 的做法。

就是直接令 $f_{u,0/1}$ 表示考虑 $u$ 这个子树,对于 $u$ 这个节点选择/不选的最大独立集,则$$ f_{u,0} = \sum_{v} \max(f_{v,0},f_{v,1})\\ f_{u,1} = a_u + \sum_{v} f_{v,0}\\ $$ 考虑修改,发现一个点修改后只会对祖先这一部分的 dp 值进行修改。

由于下标只有 2 维,非常容易想到矩阵优化这个 dp 的转移。

而细想发现,直接将儿子的转移矩阵乘起来并不是对的。这个矩阵优化只能对一个序列有效。

但是,一个序列对应树上的什么形态?链!链让我们想到什么?重链剖分!

对于每个点,考虑如何直接的写出从 $f_{son_u}$ 转移到 $f_u$ 的转移矩阵呢?

考虑到影响矩阵的只有轻儿子的部分,即:

令 $g_{u,0/1}$ 表示考虑 $u$ 这个子树,不考虑重儿子的情况下,选/不选的最大独立集,考虑从 $g$ 转移到 $f$,则$$ f_{u,0} = g_{u,0} + \max(f_{son_u,0},f_{son_u,1})\\ f_{u,1} = g_{u,1} + f_{son_u,0} $$ 这样就可以写出转移矩阵了,注意这里的矩阵乘法是广义矩阵乘法,即$$ \begin{pmatrix} g_{u,0}&g_{u, 0}\\g_{u,1}&-inf\end{pmatrix} \times \begin{pmatrix}f_{son_u,0}&f_{son_u,1}\end{pmatrix} = \begin{pmatrix}f_{u,0}&f_{u,1}\end{pmatrix} $$ 这样,我们就可以在 $g_u$ 已知的情况下,就可以用 树链剖分+线段树维护矩阵 快速的求出 $f$ 啦!那我们考虑如何求解 $g$,

其实可以发现每次修改,$g$ 的值只会在我们跳轻边的父节点会修改,那么我们直接暴力更改 $g$ 的值即可。

复杂度 $o(2^3n\log^2n)$,可以通过。

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

const int N = 1e5 + 10;
const int inf = 1e9 + 1;

struct matrix {
    int a[2][2];
    matrix (int aa = 0, int bb = 0, int cc = 0, int dd = 0) {
        a[0][0] = aa; a[0][1] = bb; a[1][0] = cc; a[1][1] = dd;
    }
    void print () {
        for (int i = 0; i < 2; ++i) {
            for (int j = 0; j < 2; ++j) printf ("%d ", a[i][j]); printf ("\n");
        }
    }
} p[N], I;

matrix operator * (const matrix x, const matrix y) {
    matrix z;
    z.a[0][0] = max (x.a[0][0] + y.a[0][0], x.a[0][1] + y.a[1][0]);
    z.a[0][1] = max (x.a[0][0] + y.a[0][1], x.a[0][1] + y.a[1][1]);
    z.a[1][0] = max (x.a[1][0] + y.a[0][0], x.a[1][1] + y.a[1][0]);
    z.a[1][1] = max (x.a[1][0] + y.a[0][1], x.a[1][1] + y.a[1][1]);
    return z;
}

struct Node {
    int l, r; matrix x;
};

struct segment_tree {
    Node t[N << 2];
    void pushup (int x) {
        t[x].x = t[x << 1].x * t[x << 1 | 1].x;
    }
    void build (int x, int l, int r) {
        t[x].l = l; t[x].r = r;
        if (l == r) { t[x].x = p[l]; return ; }
        int mid = (l + r) >> 1;
        build (x << 1, l, mid); build (x << 1 | 1, mid + 1, r);
        pushup(x);
    }
    void update (int x, int k, matrix p) {
//      cout << x <<endl;
        if (t[x].l == t[x].r) {
            t[x].x = p; return ;
        } int mid = (t[x].l + t[x].r) >> 1;
        if (k <= mid) update (x << 1, k, p); 
        else update (x << 1 | 1, k, p);
        pushup (x);
    }
    matrix query (int x, int l, int r) {
//      cout << x << endl;
//      cout << t[x].l <<" "<< t[x].r << endl;  
//      cout << t[x].l << " " << t[x].r << endl; t[x].x.print(); 
        if (l <= t[x].l && t[x].r <= r) return t[x].x;
        int mid = (t[x].l + t[x].r) >> 1; matrix res = I;
        if (l <= mid) res = res * query (x << 1, l, r);
        if (r > mid) res = res * query (x << 1 | 1, l, r);
        return res;
    }
} T;

struct node {
    int to, nxt;
} edge[N << 1];
int cnt, head[N];

int a[N], f[N][2], g[N][2];

int tim, dfn[N], low[N];
int F[N], sz[N], tp[N], son[N];

void add (int u, int v) {
    cnt ++;
    edge[cnt].to = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}

void dfs1 (int u, int fa) {
    sz[u] = 1; F[u] = fa; 
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (v == fa) continue; dfs1(v,u);
        if (sz[son[u]] < sz[v]) son[u] = v; sz[u] += sz[v];
    }
}

void dfs2 (int u, int fa, int top) {
    dfn[u] = ++tim; tp[u] = top; low[top] = tim;
    if (son[u]) dfs2 (son[u], u, top); g[u][1] += a[u];
    for (int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].to;
        if (v == fa || v == son[u]) continue;
        dfs2 (v, u, v); g[u][0] += max (f[v][0], f[v][1]); g[u][1] += f[v][0]; 
    } p[dfn[u]] = matrix(g[u][0], g[u][0], g[u][1], -inf);
    f[u][0] = g[u][0] + max (f[son[u]][0], f[son[u]][1]); f[u][1] = g[u][1] + f[son[u]][0]; 
//  cout << u << " " << f[u][0] << " " << f[u][1] <<endl;
//  cout << u << " " << f[u][0] << " " << f[u][1] << endl;
//  cout << u << endl;; p[u].print() ; cout << endl; 
}

void update (int u, int v) {
    int s0 = 0, s1 = v - a[u]; a[u] = v;
    while (u >= 1) {
//      cout << u << " " << s0 << " " << s1 << endl;
//      cout << dfn[tp[u]] << " " << low[tp[u]] << endl;
//      cout << F[tp[u]] << endl;
        g[u][0] += s0; g[u][1] += s1;
        p[dfn[u]] = matrix(g[u][0], g[u][0], g[u][1], -inf);
        matrix lst = T.query (1, dfn[tp[u]], low[tp[u]]);
        T.update (1, dfn[u], p[dfn[u]]);
        matrix use = T.query (1, dfn[tp[u]], low[tp[u]]);
//      cout << u << " "<<g[u][0] << " " << g[u][1] << endl;
        s0 = max (use.a[0][0], use.a[1][0]) - max (lst.a[0][0], lst.a[1][0]);
        s1 = use.a[0][0] - lst.a[0][0]; u = F[tp[u]];
//      cout << use.a[0][0] << " " << use.a[1][0] << endl;
//      cout << lst.a[0][0] << " " << lst.a[1][0] << endl;
    }
}

int main() {
    I = matrix (0, -inf, -inf, 0);
    int n, m; scanf ("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf ("%d", &a[i]);
    for (int i = 1; i < n; ++i) {
        int u, v; scanf ("%d%d", &u, &v); add (u, v); add (v, u);
    } dfs1 (1, 0); dfs2 (1, 0, 1); T.build (1, 1, n);
//  for (int i = 1; i <= n; ++i) printf ("%d ", son[i]); printf ("\n");
//  for (int i = 1; i <= n; ++i) printf ("%d %d\n", dfn[i], low[i]); 
    for (int Q = 1; Q <= m; ++Q) {
        int x, t; scanf ("%d%d",&x,&t); update (x, t);
        matrix ans = T.query (1, dfn[1], low[1]);
        printf ("%d\n", max (ans.a[0][0], ans.a[1][0]));
    }
    return 0;
}
posted @ 2023-11-09 13:43  wangzhongyuan  阅读(60)  评论(0)    收藏  举报  来源