• [Luogu P6021]洪水(ddp)


    题目描述

    小 A 走到一个山脚下,准备给自己造一个小屋。这时候,小 A 的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小 A 面前出现了一个瀑布。作为平民的小 A 只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个 nn 个节点的树,每个节点有权值(爬上去的代价)。小 A 要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小 A 的朋友觉得这样子太便宜小 A 了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小 A 觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。

    Solution

    也是挺基础的一道ddp入门题。 状态比模板题还少一维。

    设$dp_i$代表把$i$与它的子树内的叶节点断开所需的最少距离,有$dp_u=min(sum dp_v, a_u)$ 还是很套路地把它拆成和重儿子相关和不相关。

    $g_u=sum_{v e son(u)} dp_v,dp_u=min(g_u+dp_{son(u)},a_u+0)$。

    转移矩阵就很好构造了,代码和模板题大同小异。

    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    
    typedef long long ll;
    
    const int N = 200010;
    
    namespace IO{
        template <typename T> void read(T &x) {
            T f = 1;
            char ch = getchar();
            for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
            for (x = 0; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
            x *= f;
        }
        template <typename T> void write(T x) {
            if (x > 9) write(x / 10);
            putchar(x % 10 + '0');
        }
        template <typename T> void print(T x) {
            if (x < 0) x = -x, putchar('-');
            write(x);
            putchar('
    ');
        }
    } using namespace IO;
    
    int n, m;
    ll a[N];
    
    namespace QXX{
        struct node{
            int pre, to;
        }edge[N << 1];
        int head[N], tot;
        inline void add(int u, int v) {
            edge[++tot] = node{head[u], v};
            head[u] = tot;
        }
    }using namespace QXX;
    
    namespace MATRIX{
        template <typename T> void cmin(T &x, T y) {if (y < x) x = y;}
        template <typename T> void cmax(T &x, T y) {if (y > x) x = y;}
        struct Matrix{
            ll arr[3][3];
        };
        inline void init(Matrix &X) {
            memset(X.arr, 0x3f, sizeof(X.arr));
        }
        inline Matrix Mul(Matrix X, Matrix Y) {
            Matrix Z;
            init(Z);
            for (int i = 1; i <= 2; i++)
                for (int j = 1; j <= 2; j++)
                    for (int k = 1; k <= 2; k++)
                        cmin(Z.arr[i][j], X.arr[i][k] + Y.arr[k][j]);
            return Z;
        }
        inline void deburg(Matrix X) {
            for (int i = 1; i <= 2; i++) {
                for (int j = 1; j <= 2; j++) {
                    cout << X.arr[i][j] << " ";
                } cout << endl;
            } cout << endl;
        }
    }using namespace MATRIX;
    
    namespace TCP{
        Matrix A[N];
        ll dp[N];
        int dfn[N], pos[N], top[N], End[N], num;
        int sz[N], fa[N], son[N];
        void dfs1(int x) {
            sz[x] = 1;
            for (int i = head[x]; i; i = edge[i].pre) {
                if (edge[i].to == fa[x]) continue;
                fa[edge[i].to] = x;
                dfs1(edge[i].to);
                sz[x] += sz[edge[i].to];
                if (sz[edge[i].to] > sz[son[x]]) son[x] = edge[i].to;
            }
        }
        void dfs2(int x, int chain) {
            dfn[x] = ++num, top[x] = chain, pos[dfn[x]] = x;
            init(A[x]);
            A[x].arr[1][1] = 0, A[x].arr[1][2] = a[x];
            A[x].arr[2][2] = 0;
            if (son[x])
                dfs2(son[x], chain);
            else
                End[chain] = dfn[x], A[x].arr[1][1] = a[x], A[x].arr[2][1] = 0;
            for (int i = head[x]; i; i = edge[i].pre) {
                if (edge[i].to == fa[x] || edge[i].to == son[x]) continue;
                dfs2(edge[i].to, edge[i].to);
                A[x].arr[1][1] += dp[edge[i].to];
            }
            if (son[x]) dp[x] = min(a[x], A[x].arr[1][1] + dp[son[x]]);
            else dp[x] = a[x];
        }
    }using namespace TCP;
    
    namespace Segment_Tree{
        struct Segment{
            Matrix val;
        }tr[N << 2];
        inline void push_up(int p) {tr[p].val = Mul(tr[p << 1].val, tr[p << 1 | 1].val);}
        void build(int p, int l, int r) {
            if (l == r) {
                tr[p].val = A[pos[l]];
                return;
            }
            int mid = (l + r) >> 1;
            build(p << 1, l, mid);
            build(p << 1 | 1, mid + 1, r);
            push_up(p);
        }
        void change(int p, int l, int r, int Pos) {
            if (l == r) {
                tr[p].val = A[pos[l]];
                return;
            }
            int mid = (l + r) >> 1;
            if (Pos <= mid) change(p << 1, l, mid, Pos);
            else change(p << 1 | 1, mid + 1, r, Pos);
            push_up(p);
        }
        Matrix query(int p, int l, int r, int L, int R) {
            if (L <= l && r <= R) {
                return tr[p].val;
            }
            int mid = (l + r) >> 1;
            if (R <= mid) return query(p << 1, l, mid, L, R);
            else if (L > mid) return query(p << 1 | 1, mid + 1, r, L, R);
            else return Mul(query(p << 1, l, mid, L, mid), query(p << 1 | 1, mid + 1, r, mid + 1, R));
        }
    }using namespace Segment_Tree;
    
    inline void update(int x, ll val) {//将节点 a 的权值修改为 b 
        A[x].arr[1][2] += (val - a[x]);
        if (!son[x]) A[x].arr[1][1] = A[x].arr[1][2];
        a[x] = val;
        while (x) {
            Matrix bef = query(1, 1, n, dfn[top[x]], End[top[x]]);
            change(1, 1, n, dfn[x]);
            Matrix aft = query(1, 1, n, dfn[top[x]], End[top[x]]);
            x = fa[top[x]];
            A[x].arr[1][1] += aft.arr[1][1] - bef.arr[1][1];
            
        }
    }
    
    int main() {
        read(n);
        for (int i = 1; i <= n; i++) read(a[i]);
        for (int i = 1, u, v; i < n; i++) read(u), read(v), add(u, v), add(v, u);
        read(m);
        dfs1(1);
        dfs2(1, 1);
        build(1, 1, n);
        while (m--) {
            char opt[2];
            int x;
            scanf("%s", opt + 1); read(x);
            if (opt[1] ==  'Q') {
                Matrix ans = query(1, 1, n, dfn[x], End[top[x]]);
                print(ans.arr[1][1]);
            } else {
                int val;
                read(val);
                update(x, a[x] + val);
            }
        }
        return 0;
    }
  • 相关阅读:
    C++中数字与字符串之间的转换
    关于安卓启动eclipse错误:找不到元素‘d:devices'的声明
    linux相关解压命令
    泛型
    CRUD工程师---番外篇---反射
    CRUD工程师---设计模式
    AOP切面日志
    CRUD工程师---InnoDB存储引擎
    CRUD工程师---InnoDB存储引擎2
    CRUD工程师---InnoDB存储引擎4(表)
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/13101577.html
Copyright © 2020-2023  润新知