• Luogu P4643 【模板】动态dp(矩阵乘法,线段树,树链剖分)


    题面

    给定一棵 (n) 个点的树,点带点权。

    (m) 次操作,每次操作给定 (x,y) ,表示修改点 (x) 的权值为 (y)

    你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

    题解

    如题所示 , 是个模板题 ...

    首先考虑静态 (dp) , 令 (dp_{u,0/1})(u) 不存在 / 存在 于最大权独立集的权值大小 .

    然后转移很显然 , 一个点存在于独立集中时 , 儿子全都不能选 . 不存在时 , 儿子可选可不选 .

    (v)(u) 的儿子 , 那么转移就有 :

    [egin{cases} displaystyle dp_{u,0} = sum_{v} max {dp_{v,1} dp_{v,0}} \ displaystyle dp_{u,1} = val_u + sum_v dp_{u,0} end{cases} ]

    这个如果每次修改直接做是 (O(n^2)) 的 , 不能满足要求 .

    然而此题是随机数据 . 每次修改单点权值 , 只需要修改这个点到根的 (dp) 值就行了 , 期望复杂度 (O(q log n + n)) .

    然后跑的飞快 , 怒拿 Luogu rank2.... 但这种随便一条链 , 或者扫帚就挂了 ..

    下面就要引入正解了 .

    也就是对于这种只有加法和取 (max) 操作的 (dp) 我们可以考虑构造矩阵 .

    也就是普通矩阵乘法进行改变 , 把 ( imes) 变成 (+) , (+) 变成 (max) .

    也就是 (displaystyle C_{i,j} = sum_{k} A_{i,k} imes B_{k,j}) 变成 (displaystyle C_{i,j} = max_{k} {A_{i,k}+B_{k,j}}) 就行了.

    不难发现这个仍然满足结合律 . (可以用展开来证明 , 或者类似于 (Floyd) 的方法理解)

    首先树链剖分 , 考虑链上如何修改和询问 . 不难构造矩阵 .

    [egin{bmatrix} 0 & 0\ val_u & -infty end{bmatrix} imes egin{bmatrix} dp_{i,0} \ dp_{i,1}end{bmatrix} = egin{bmatrix} dp_{i,0} \ dp_{i,1} end{bmatrix} ]

    然后我们考虑用线段树维护前面那个矩阵 , 然后每次 (O(log n)) 修改和询问就行了 .

    但扩展到树上的时候有些麻烦 , 因为对于一个点可能存在多个儿子 .

    但此时它最多只会有一个重儿子 , 我们考虑记下他所有的轻儿子对这个点贡献后的矩阵就行了.

    也就是说 令 (g_{u,0/1}) 为之前 (dp_{u, 0/1}) 去掉重儿子得到的答案 , 那么此时的转移矩阵就变成了 .

    [egin{bmatrix} g_{u,0} & g_{u,0}\ g_{u,1} & 0 end{bmatrix} imes egin{bmatrix} dp_{son[u],0} \ dp_{son[u],1}end{bmatrix} = egin{bmatrix} dp_{u,0} \ dp_{u,1} end{bmatrix} ]

    请自己展开验证...

    然后每次修改的时候 , 只需要在它重链底端的矩阵上进行修改就行了 . (第一个点直接改本身)

    修改的话 , 就是得到原来的一个版本和新的一个版本 , 然后对于这个点减去两个版本的差值就行了 .

    注意这个版本是意味着这整条重链的版本 , 因为我们要求的是链顶端所有子树最后贡献出来的 (dp) 值.

    询问的话直接询问 (1) 所在重链的答案 . 然后取它选与不选的 (max) 就行了.

    注意线段树中的询问 , 最好别写单位矩阵分别乘上左右两边的写法 , 常数会陡增 !!

    但还是介绍一下单位矩阵 ... (自己推得qwq) 对角线全都是 (0) , 其他位置全是 (-infty) .

    也就是 $$displaystyle egin{bmatrix} 0 & -infty & cdots & -infty -infty & 0 & cdots & -infty vdots & cdots & ddots & vdots -infty & -infty & cdots & -infty -infty & -infty & cdots & 0end{bmatrix}$$

    最后时间复杂度就是 (O(n log^2 n)) 的... 有点恐怖 , 但跑的还是算快的 . 矩阵乘法那里有 (8) 的常数有点伤 .

    代码

    #include <bits/stdc++.h>
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    using namespace std;
    
    bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
    
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0');
        return x * fh;
    }
    
    void File() {
    #ifdef zjp_shadow
        freopen ("P4643.in", "r", stdin);
        freopen ("P4643.out", "w", stdout);
    #endif
    }
    
    const int N = 1e5 + 1e3, Lim = 2, inf = 0x7f7f7f7f;
    
    struct Matrix { 
    	int a[Lim + 1][Lim + 1]; 
    
    	Matrix () { Set(a, 0); } 
    
    	void Unit() { For (i, 1, Lim) For (j, 1, Lim) a[i][j] = (i == j) ? 0 : -inf; }
    } ;
    
    inline Matrix operator * (Matrix a, Matrix b) {
        Matrix res;
    	For (i, 1, Lim) For (j, 1, Lim) For (k, 1, Lim) chkmax(res.a[i][j], a.a[i][k] + b.a[k][j]);
        return res;
    }
    
    void Out(Matrix a) { For (i, 1, Lim) For (j, 1, Lim) printf ("%d%c", a.a[i][j], j == jend ? '
    ' : ' '); putchar ('
    '); }
    
    vector<int> G[N];
    int sz[N], son[N], fa[N];
    void Dfs_Init(int u, int from = 0) {
        sz[u] = 1; fa[u] = from;
        for (int v : G[u]) if (v ^ from) {
            Dfs_Init(v, u), sz[u] += sz[v];
            if (sz[v] > sz[son[u]]) son[u] = v;
        }
    }
    
    int dfn[N], id[N], top[N], back[N];
    void Dfs_Part(int u) {
        static int clk = 0;
        id[dfn[u] = ++ clk] = u;
        top[u] = son[fa[u]] == u ? top[fa[u]] : u;
        back[top[u]] = u; if (son[u]) Dfs_Part(son[u]);
        for (int v : G[u]) if ((v ^ fa[u]) && (v ^ son[u])) Dfs_Part(v);
    }
    
    int dp[N][2], val[N];
    void Dp_Pre(int u) {
        int summax = 0, sum0 = val[u];
        for (int v : G[u]) if (v ^ fa[u])
            Dp_Pre(v), sum0 += dp[v][0], summax += max(dp[v][0], dp[v][1]);
        dp[u][0] = summax, dp[u][1] = sum0;
    }
    
    Matrix sub[N];
    
    #define lson o << 1, l, mid
    #define rson o << 1 | 1, mid + 1, r
    struct Segment_Tree {
        Matrix Adv[N << 2];
    
        inline void push_up(int o) { Adv[o] = Adv[o << 1] * Adv[o << 1 | 1]; }
    
        void Build(int o, int l, int r) {
            if (l == r) {
                int u = id[l], summax = 0, sum0 = val[u];
                for (int v : G[u]) if ((v ^ fa[u]) && (v ^ son[u]))
                    summax += max(dp[v][0], dp[v][1]), sum0 += dp[v][0];
                Adv[o].a[1][1] = Adv[o].a[1][2] = summax; Adv[o].a[2][1] = sum0;
                sub[u] = Adv[o]; return ;
            }
            int mid = (l + r) >> 1; Build(lson); Build(rson); push_up(o);
        }
    
        void Update(int o, int l, int r, int up) {
            if (l == r) { Adv[o] = sub[id[l]]; return ; }
            int mid = (l + r) >> 1;
            if (up <= mid) Update(lson, up); else Update(rson, up); push_up(o);
        }
    
        Matrix Query(int o, int l, int r, int ql, int qr) {
            if (ql <= l && r <= qr) return Adv[o];
            int mid = (l + r) >> 1; 
    		/*
    		   Matrix tmp; tmp.Unit();
    		   if (ql <= mid) tmp = tmp * Query(lson, ql, qr);
    		   if (qr > mid) tmp = tmp * Query(rson, ql, qr);
    		 */
    		if(qr <= mid) return Query(lson, ql, qr);
            if(ql > mid) return Query(rson, ql, qr);
            return Query(lson, ql, qr) * Query(rson, ql, qr);
        }
    } T;
    
    int n, m;
    
    inline void Update(int pos, int uv) {
        sub[pos].a[2][1] += - val[pos] + uv, val[pos] = uv;
        Matrix bef, aft;
        while (pos) {
            bef = T.Query(1, 1, n, dfn[top[pos]], dfn[back[top[pos]]]);
            T.Update(1, 1, n, dfn[pos]);
            aft = T.Query(1, 1, n, dfn[top[pos]], dfn[back[top[pos]]]);
            pos = fa[top[pos]];
    
            sub[pos].a[1][2] = (sub[pos].a[1][1] += - max(bef.a[1][1], bef.a[2][1]) + max(aft.a[1][1], aft.a[2][1]));
            sub[pos].a[2][1] += - bef.a[1][1] + aft.a[1][1];
        }
    }
    
    inline int Query() {
        Matrix tmp = T.Query(1, 1, n, dfn[1], dfn[back[top[1]]]);
        return max(tmp.a[1][1], tmp.a[2][1]);
    }
    
    int main () {
    	File();
    
        n = read(); m = read();
        For (i, 1, n) val[i] = read();
        For (i, 1, n - 1) { int u = read(), v = read(); G[u].push_back(v); G[v].push_back(u); }
    
        Dfs_Init(1); Dfs_Part(1); Dp_Pre(1); T.Build(1, 1, n);
    
        while (m --) {
            int x = read(), y = read();
            Update(x, y);
            printf ("%d
    ", Query());
        }
    
    	//cerr << (double) clock() / CLOCKS_PER_SEC << endl;
        return 0;
    }
    
  • 相关阅读:
    alter table add column default
    JS小知识点
    java list按照元素对象的指定多个字段属性进行排序
    Web.xml配置详解之context-param
    spring在web.xml中的配置
    Activiti工作流(4):编写一个HelloWorld
    Activiti工作流(3):activiti核心API
    Activiti工作流框架学习(二)——使用Activiti提供的API完成流程操作
    Activiti工作流框架学习(一)——环境的搭建和数据表的了解
    Redis系列之(二):Redis主从同步,读写分离
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9200826.html
Copyright © 2020-2023  润新知