• 「NOIP2018」保卫王国


    「NOIP2018保卫王国」

    题目描述
    有一棵 (n) 个点, 点有点权 (a_i)(m) 组询问, 每次求钦点两个节点必须选或者必须不选后的树上最小点覆盖。

    (1 leq n, m leq 10^5)

    解题思路 :

    这个题唯一的意义恐怕是普及了一个还不能算太普及的科技,至少我没有时间去实现这个东西。当然 ( ext{nqiiii}) 大爷考场上写了标算没写这个科技就过了是真的强。(不愧是机房里仅次于 ( ext{AK})( ext{zzd}) 的男人)

    我最早接触这个科技是联赛前一个星期 ( ext{txc}) 让我膜他博客的时候接触到的,感觉他博客讲的非常优秀,现在这里挂一个链接传送一下:撩妹狂魔的博客

    说普及科技的原因是因为这个题用动态( ext{DP})来做基本就是一个模板题。首先不考虑询问,问题是一个简单的树形( ext{DP}) ,设 (f[u][0/1]) 表示以 (u) 为根的子树里面 (u) 不选的最小点覆盖。

    [f[u][0] = sum_{u ightarrow v} f[v][1] \ f[u][1] = a_i +sum_{u ightarrow v} min(f[v][0], f[v][1]) ]

    直接处理可能不太方便,不妨先考虑退化成一条 (i ightarrow i+1) 的链的情况:

    [f[i][0] = f[i-1][1] \ f[i][1] = a_i + min(f[i-1][0], f[i-1][1]) ]

    考虑这个 ( ext{DP}) 只有加法和取 ( ext{min}) 操作,不妨用重新定义线性变换来描述这个转移:

    [egin{bmatrix} 0 & infty \ a_i & a_i end{bmatrix} egin{bmatrix} f[i-1][0] \ f[i-1][1] end{bmatrix} = egin{bmatrix} f[i][0] \ f[i][1] end{bmatrix} ]

    这里把乘法重定义为加法,加法重定义为取 (min),这个矩阵的乘法就等价于上式的转移。然后用线段树维护一下就可以做原题中链的部分分了。

    考虑树的情况,既然会做链了,那么只需要树剖就能搞了。用线段树维护每一条重链上的转移,转移的时候需要将这个节点的轻儿子的贡献写到转移矩阵中,那么只需要先预处理出 (f) ,设:

    [A_u = sum_{u ightarrow v, v eq mson} f[v][1] \ B_u = a_u + sum_{u ightarrow v, v eq mson} min(f[v][1], f[v][1]) ]

    原来的线性变换就改为:

    [egin{bmatrix} A_u & infty \ B_u & B_u end{bmatrix} egin{bmatrix} f[mson][0] \ f[mson][1] end{bmatrix} = egin{bmatrix} f[u][0] \ f[u][1] end{bmatrix} ]

    其中 (mson) 表示 (u) 的重儿子,此时每一个点的 (f) 就是其所在重链底部到它的所有矩阵乘起来的结果,用线段树维护即可。

    考虑怎么回答这个题的询问,每次修改可以看做将两个 (f[u][s]) 修改为 (infty) 后求答案,这里等价于修改 (A_u)或者(B_u) ,考虑一次修改的影响是到当前重链的链头的,所以每跳一次轻边都要重新计算父亲的 (A_u)(B_u) ,这样相当于是在线段树上做单点修改,并求出修改完后 (f[1]) 的值。考虑轻边最多跳 (log) 次,修改的总复杂度是 (O(log^2n)),总复杂度是 (O(nlog^2n))

    实际上这个东西还有两个 (O(nlogn)) 的做法,很显然其中一个是 ( ext{Lct}),另外一个是叫全局平衡二叉树的科技,$ ext{txc} $的博客里讲的非常优秀,大家可以去膜拜一波。


    /*program by mangoyang*/
    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    #define inf ((ll)(1e10))
    #define Max(a, b) ((a) > (b) ? (a) : (b))
    #define Min(a, b) ((a) < (b) ? (a) : (b))
    typedef long long ll;
    using namespace std;
    template <class T>
    inline void read(T &x){
        int ch = 0, f = 0; x = 0;
        for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
        for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
        if(f) x = -x;
    }
    const int N = 200005;
    char str[10];
    vector<int> ed[N];
    ll f[N][2], g[N][2], ff[N], a[N];
    int top[N], sz[N], ls[N], ms[N], dfn[N], n, m, cnt;
    struct Matrix{
        ll a[3][3];
        inline Matrix(){ 
            for(int i = 1; i <= 2; i++)
                for(int j = 1; j <= 2; j++) a[i][j] = inf;
        }
        inline Matrix(ll x, ll y){
            a[1][2] = x, a[2][1] = a[2][2] = y, a[1][1] = inf;
        }
        Matrix operator * (const Matrix &A) const{
            Matrix c;
            for(int i = 1; i <= 2; i++)
                for(int j = 1; j <= 2; j++) 
                    for(int k = 1; k <= 2; k++)
                        c.a[i][j] = Min(a[i][k] + A.a[k][j], c.a[i][j]);
            return c;
        }
    };
    struct SegmentTree{
        Matrix s[N<<2];
        #define lson (u << 1)
        #define rson (u << 1 | 1)
        inline void modify(int u, int l, int r, int pos, Matrix x){
            if(l == r) return (void) (s[u] = x);
            int mid = l + r >> 1;
            if(pos <= mid) modify(lson, l, mid, pos, x);
            else modify(rson, mid + 1, r, pos, x);
            s[u] = s[lson] * s[rson];
        }
        inline Matrix query(int u, int l, int r, int L, int R){
            if(l >= L && r <= R) return s[u];
            int mid = l + r >> 1;
            Matrix res; 
            res.a[1][1] = res.a[2][2] = 0, res.a[1][2] = res.a[2][1] = inf;
            if(L <= mid) res = res * query(lson, l, mid, L, R);
            if(mid < R) res = res * query(rson, mid + 1, r, L, R);
            return res;
        }
    }Seg;
    namespace gao{
        inline void dfs(int u, int fa){
            ff[u] = fa, sz[u] = 1, f[u][1] = a[u];
            for(int i = 0; i < ed[u].size(); i++){
                int v = ed[u][i];
                if(v == fa) continue;
                dfs(v, u), sz[u] += sz[v];
                if(sz[v] > sz[ms[u]]) ms[u] = v;
                f[u][0] += f[v][1];
                f[u][1] += min(f[v][0], f[v][1]);
            }
        }
        inline void dfs2(int u, int fa, int fff){
            top[u] = fff, dfn[u] = ++cnt, g[u][1] = a[u];
            if(!ms[u]){
                Seg.modify(1, 1, n, dfn[u], Matrix(g[u][0], g[u][1]));
                return (void) (ls[fff] = cnt);
            }
            dfs2(ms[u], u, fff);
            for(int i = 0; i < ed[u].size(); i++){
                int v = ed[u][i];
                if(v == ms[u] || v == fa) continue;
                g[u][0] += f[v][1];
                g[u][1] += min(f[v][1], f[v][0]);
                dfs2(v, u, v);
            }	
            Seg.modify(1, 1, n, dfn[u], Matrix(g[u][0], g[u][1]));
        }
        inline ll update(int u){
            Seg.modify(1, 1, n, dfn[u], Matrix(g[u][0], g[u][1]));
            for(u = top[u]; u > 1; u = top[ff[u]]){
                Matrix now = Seg.query(1, 1, n, dfn[u], ls[u]);
                g[ff[u]][0] -= f[u][1];
                g[ff[u]][1] -= Min(f[u][0], f[u][1]);
                f[u][0] = now.a[1][2], f[u][1] = now.a[2][2];
                g[ff[u]][0] += f[u][1];
                g[ff[u]][1] += Min(f[u][0], f[u][1]);
                Seg.modify(1, 1, n, dfn[ff[u]], Matrix(g[ff[u]][0], g[ff[u]][1]));
            }
            Matrix ans = Seg.query(1, 1, n, 1, ls[1]);
            return min(ans.a[1][2], ans.a[2][2]);
        }
    }
    signed main(){
        read(n), read(m), scanf("%s", str);
        for(int i = 1; i <= n; i++) read(a[i]);
        for(int i = 1, x, y; i < n; i++){
            read(x), read(y);
            ed[x].push_back(y), ed[y].push_back(x);
        }
        gao::dfs(1, 0), gao::dfs2(1, 0, 1);
        for(int i = 1, a, b, x, y; i <= m; i++){
            read(a), read(x), read(b), read(y), x ^= 1, y ^= 1;
            ll tmp1 = g[a][x], tmp2 = g[b][y];
            g[a][x] = inf, gao::update(a);
            g[b][y] = inf; ll ans = gao::update(b);
            if(ans >= inf) puts("-1"); else printf("%lld
    ", ans);
            g[a][x] = tmp1, gao::update(a);
            g[b][y] = tmp2, gao::update(b);
        }
        return 0;
    }
    
  • 相关阅读:
    MySQL-EXPLAIN执行计划字段解释
    MySQL-EXPLAIN执行计划Extra解释
    HTTP与HTTPS的区别
    【面试】Java中sleep和wait的区别
    Nginx之前后端分离(入门)
    玩程序 之 一 . 字符串处理工具(可通过C#脚本扩展)
    C#实现下载功能,可用于软件自动更新
    对c#剪切板Clipboard占用的问题一点解决方法
    C# 制作 仪表
    C#使用自定义字体(从文件获取)
  • 原文地址:https://www.cnblogs.com/mangoyang/p/10004312.html
Copyright © 2020-2023  润新知