zoukankan      html  css  js  c++  java
  • BZOJ 2286 消耗战

    传送门

    题目大意:

    每次给出k个特殊点,回答将这些特殊点与根节点断开至少需要多少代价。

    题目分析:

    虚树入门 + 树型dp:
    刚刚学习完虚树(好文),就来这道入门题签个到。
    虚树就是将树中的一些关键点提取出来,在不改变父子关系的条件下用$O(mlog n) (组成一颗新树(m特殊点数,n总数),大小为)O(m)$,以便降低后续dp等的复杂度。
    建好虚树过后就可以进行普通的dp了(mn[u]表示原图中u到根节点的最短边长):

    [dp[u] = mn[u] (u是特殊点) ]

    [dp[u] = min(mn[u], sum{dp[son[u]]}) (u不是特殊点) ]

    此题就当做是虚树模板了。

    注意每一次重建虚树时不用将1~n的边信息全部清空,不然你会见识到clear的惊人速度(T飞)。

    code

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 250050
    #define oo 0x3f3f3f3f
    typedef long long ll;
    typedef pair<int, ll> pil;
    namespace IO{
        inline int read(){
            int i = 0, f = 1; char ch = getchar();
            for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
            if(ch == '-') f = -1, ch = getchar();
            for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
            return i * f;
        }
        inline void wr(ll x){
            if(x < 0) x = -x, putchar('-');
            if(x > 9) wr(x / 10);
            putchar(x % 10 + '0');
        } 
    }using namespace IO;
    int n, m;
    vector<pil> g[maxn];
    vector<int> vg[maxn];
    ll dp[maxn], mn[maxn];
    int dfn[maxn], clk, dep[maxn], vir[maxn], virCnt, par[maxn], rt;
    bool key[maxn];
    namespace LCA{
        int pos[maxn], top[maxn], son[maxn], sze[maxn], tot, fa[maxn];
        inline void dfs1(int u, int f){
            dfn[u] = ++clk;
            dep[u] = dep[f] + 1;
            fa[u] = f;
            sze[u] = 1;
            for(int i = g[u].size() - 1; i >= 0; i--){
                int v = g[u][i].first;
                if(v == f) continue;
                mn[v] = min(mn[u], g[u][i].second);
                dfs1(v, u);
                sze[u] += sze[v];
                if(sze[v] > sze[son[u]] || !son[u]) son[u] = v;
            }
        }
        inline void dfs2(int u, int f){
            if(son[u]){
                pos[son[u]] = ++tot;
                top[son[u]] = top[u];
                dfs2(son[u], u);
            }
            for(int i = g[u].size() - 1; i >= 0; i--){
                int v = g[u][i].first;
                if(v == f || v == son[u]) continue;
                pos[v] = ++tot;
                top[v] = v;
                dfs2(v, u);
            }
        }
        inline void splitTree(){
            dfs1(1, 0);
            pos[tot = 1] = top[1] = 1;
            dfs2(1, 0);
        }
        inline int getLca(int u, int v){
            while(top[u] != top[v]){
                if(dep[top[u]] < dep[top[v]]) swap(u, v);
                u = fa[top[u]];
            }
            return dep[u] < dep[v] ? u : v;
        }
    }
     
    inline bool cmp(int u, int v){
        return dfn[u] < dfn[v];
    }
     
    inline void buildVir(){
        static int stk[maxn], top;
        top = 0;
        sort(vir + 1, vir + virCnt + 1, cmp);
        int oriSze = virCnt;
        for(int i = 1; i <= oriSze; i++){
            int u = vir[i];
            if(!top){
                stk[++top] = u;
                par[u] = 0;
                continue;
            }
            int lca = LCA::getLca(stk[top], u);
            while(dep[lca] < dep[stk[top]]){
                if(dep[stk[top - 1]] < dep[lca]) par[stk[top]] = lca;
                --top;
            }
            if(lca != stk[top]){
                vir[++virCnt] = lca;
                par[lca] = stk[top];
                stk[++top] = lca;
            }
            par[u] = lca;
            stk[++top] = u;
        }
        for(int i = 1; i <= virCnt; i++) vg[vir[i]].clear();
        for(int i = 1; i <= virCnt; i++){
            int u = vir[i];
            key[u] = ((i <= oriSze) ? 1 : 0);
            if(par[u]) vg[par[u]].push_back(u);
        }
        sort(vir + 1, vir + virCnt + 1, cmp);
    }
     
    inline void DP(int u){
    //  cout<<u<<"!";
        ll ret = 0;
        for(int i = vg[u].size() - 1; i >= 0; i--){
            int v = vg[u][i];
            DP(v);
            ret += dp[v];
        }
        if(key[u]) dp[u] = mn[u];
        else dp[u] = min(mn[u], ret);
    }
     
    inline void solve(){
        buildVir();
        DP(vir[1]);
        wr(dp[vir[1]]);
        putchar('
    ');
    }
     
    int main(){
    	freopen("h.in", "r", stdin);
        n = read();
        for(int i = 1; i < n; i++){
            int x = read(), y = read();
            ll c = 1ll * read();
            g[x].push_back(pil(y, c));
            g[y].push_back(pil(x, c));
        }
        memset(mn, oo, sizeof mn);
        LCA::splitTree();
        m = read();
        for(int i = 1; i <= m; i++){
            int k = read();
            virCnt = 0;
            for(int j = 1; j <= k; j++)
                vir[++virCnt] = read();
            solve();
        }
        return 0;
    }
    
  • 相关阅读:
    动态代理Dynamic Proxy
    ORM SQLOBJECT SIMPLE
    python mysql desc
    How to use AKBusGpsParser
    AT&T ASSEMBLY FOR LINUX AND MAC (SYS_FORK)
    How to install ZeroMQ on Ubuntu14.04
    [LeetCode]208. 实现 Trie (前缀树)
    [LeetCode]438. 找到字符串中所有字母异位词、76. 最小覆盖子串(滑动窗口解决子串问题系列)
    【二叉树-最长路径系列(任意路径)】直径、最长同值路径、 最大路径和(DFS、树形DP)
    [LeetCode]146. LRU缓存机制
  • 原文地址:https://www.cnblogs.com/CzYoL/p/7712036.html
Copyright © 2011-2022 走看看