【學習筆記】動態DP

引子

在這裏插入圖片描述


如果把修改扔開,這就是一道十分經典的樹形DP入門題。
然而加上修改,這道題瞬間毒瘤了起來。
先把轉移方程擺在下面:
f[i][0]=jsonmax(f[j][0],f[j][1])f[i][1]=jsonf[j][0]+w[i]f[i][0]=\sum_{j \in son}max(f[j][0],f[j][1]) \\ f[i][1]=\sum_{j \in son} f[j][0]+w[i]
我們首先考慮處理一種特殊的情況——樹形成了一條鏈。這樣一來,這個轉移式就可以化簡:
f[i][0]=max(f[j][0],f[j][1])f[i][1]=f[j][0]+w[i]f[i][0]=max(f[j][0],f[j][1]) \\ f[i][1]=f[j][0]+w[i]
這樣一來,我們嘗試用一個矩陣運算來轉移:
(00w[i])op(f[j][0]f[j][1])=(f[i][0]f[i][1]) \begin{pmatrix} 0&0 \\ w[i]& -\infin \end{pmatrix}op \begin{pmatrix}f[j][0] \\ f[j][1] \end{pmatrix}=\begin{pmatrix} f[i][0] \\ f[i][1] \end{pmatrix}
其中,A op B=CA\ op\ B=C定義爲:
Ci,j=max{Ai,k+Bk,jk[1,n]} C_{i,j}=\max\{ A_{i,k}+B_{k,j}| k \in [1,n]\}
那麼,對於一個修改操作,就可以直接更新這個矩陣,用線段樹維護區間opop,這樣就解決了這個問題。


現在考慮一般的樹。對於一個節點ii,我們暫時先考慮其中的一個兒子jj,那麼就能得到:
(f[i][0]f[i][0]f[i][1])op(f[j][0]f[j][1])=(f[i][0]f[i][1]) \begin{pmatrix} f'[i][0]&f'[i][0] \\ f'[i][1]&-\infin \end{pmatrix} op \begin{pmatrix}f[j][0] \\ f[j][1]\end{pmatrix}=\begin{pmatrix} f[i][0] \\ f[i][1] \end{pmatrix}
ff'表示考慮除jj外的兒子的答案。
在這裏,jj的地位如此特殊,便不難想到對這棵樹做樹鏈剖分,jj就是ii的重兒子。那麼對於重鏈上的信息,模仿上述序列的方法維護;而輕兒子的信息則暴力上傳即可。


接下來正式講講實現。
對於修改操作,我們直接修改目標位置的矩陣。此時,只有該位置的祖先的答案纔有可能變化。於是,修改矩陣以後,更新它所在重鏈的信息,再跳轉至新的重鏈上。此時,原來重鏈的頂端就變爲了該點的輕兒子,故暴力上傳信息即可。之後重複這一過程。
對於查詢操作,由於每一處的最佳決策均轉移至根節點所在重鏈上,所直接查詢根節點所在重鏈即可。總的時間複雜度爲O(nlog22n)O(n\log_2^2n)

#include<bits/stdc++.h>
#define lch (i << 1)
#define rch ((i << 1) | 1)
#define mid ((t[i].l + t[i].r) >> 1)
using namespace std;
const int mn = 100005, inf = -(1 << 30);
struct matrix{
    int a[2][2];
    matrix() {memset(a, 0, sizeof a);}
    matrix(int x, int y, int z, int w) {a[0][0] = x, a[0][1] = y, a[1][0] = z, a[1][1] = w;}
    matrix operator* (const matrix b) const
    {
        matrix ret;
        for(int i = 0; i < 2; i++)
            for(int j = 0; j < 2; j++)
                for(int k = 0; k < 2; k++)
                    ret.a[i][j] = max(ret.a[i][j], a[i][k] + b.a[k][j]);
        return ret;
    }
}tmp[mn];
struct seg{
    int l, r;
    matrix val;
}t[mn << 3];
struct edge{
    int to, nxt;
}e[mn << 1];
int fir[mn], cnt;
int v[mn], f[mn][2];
int fa[mn], siz[mn], son[mn], top[mn], num[mn], pos[mn], bot[mn], times;
inline void addedge(int a, int b) {e[++cnt] = (edge) {b, fir[a]}, fir[a] = cnt;}
inline int getint()
{
    int ret = 0, flg = 1; char c;
    while((c = getchar()) < '0' || c > '9')
        if(c == '-') flg = -1;
    while(c >= '0' && c <= '9')
        ret = ret * 10 + c - '0', c = getchar();
    return ret * flg;
}
void dfs1(int s, int f)
{
    fa[s] = f, siz[s] = 1;
    for(int i = fir[s]; i; i = e[i].nxt)
    {
        int t = e[i].to;
        if(t != f)
        {
            dfs1(t, s), siz[s] += siz[t];
            if(siz[son[s]] < siz[t]) son[s] = t;
        }
    }
}
void dfs2(int s)
{
    num[s] = ++times, pos[times] = bot[s] = s;
    if(son[s]) top[son[s]] = top[s], dfs2(son[s]), bot[s] = bot[son[s]];
    for(int i = fir[s]; i; i = e[i].nxt)
    {
        int t = e[i].to;
        if(t != fa[s] && t != son[s])
            top[t] = t, dfs2(t);
    }
}
void dfs(int s)
{
    f[s][1] = v[s];
    for(int i = fir[s]; i; i = e[i].nxt)
    {
        int t = e[i].to;
        if(t != fa[s])
            dfs(t), f[s][0] += max(f[t][0], f[t][1]), f[s][1] += f[t][0];
    }
}
void make_tree(int i, int l, int r)
{
    t[i] = (seg) {l, r, matrix(0, 0, 0, 0)};
    if(l == r)
    {
        int s = pos[l], g0 = 0, g1 = v[s];
        for(int i = fir[s]; i; i = e[i].nxt)
        {
            int t = e[i].to;
            if(t != fa[s] && t != son[s])
                g0 += max(f[t][0], f[t][1]), g1 += f[t][0];
        }
        tmp[l] = t[i].val = matrix(g0, g0, g1, inf);
        return;
    }
    make_tree(lch, l, mid), make_tree(rch, mid + 1, r), t[i].val = t[lch].val * t[rch].val;
}
matrix getans(int i, int l, int r)
{
    if(t[i].l == l && t[i].r == r) return t[i].val;
    if(r <= mid) return getans(lch, l, r);
    else if(l > mid) return getans(rch, l, r);
    else return getans(lch, l, mid) * getans(rch, mid + 1, r);
}
void edit_tree(int i, int p)
{
    if(t[i].l == p && t[i].r == p) {t[i].val = tmp[p]; return;}
    if(p <= mid) edit_tree(lch, p);
    else edit_tree(rch, p);
    t[i].val = t[lch].val * t[rch].val;
}
void edit(int p, int w)
{
    tmp[num[p]].a[1][0] += w - v[p], v[p] = w;
    while(p)
    {
        matrix a = getans(1, num[top[p]], num[bot[p]]);
        edit_tree(1, num[p]);
        matrix b = getans(1, num[top[p]], num[bot[p]]);
        p = fa[top[p]];
        if(!p) return;
        int x = num[p], g0 = a.a[0][0], g1 = a.a[1][0], f0 = b.a[0][0], f1 = b.a[1][0];
        tmp[x].a[0][0] = tmp[x].a[0][1] = tmp[x].a[0][0] + max(f0, f1) - max(g0, g1),
        tmp[x].a[1][0] += f0 - g0;
    }
}
int main()
{
    int n = getint(), m = getint(), a, b;
    for(int i = 1; i <= n; i++)
        v[i] = getint();
    for(int i = 1; i < n; i++)
        a = getint(), b = getint(), addedge(a, b), addedge(b, a);
    dfs1(1, 0), top[1] = 1, dfs2(1), dfs(1), make_tree(1, 1, n);
    while(m--)
    {
        a = getint(), b = getint(), edit(a, b);
        matrix ans = getans(1, num[1], num[bot[1]]);
        printf("%d\n", max(ans.a[0][0], ans.a[1][0]));
    }
}

發佈了70 篇原創文章 · 獲贊 22 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章