zoukankan      html  css  js  c++  java
  • BZOJ2286: [Sdoi2011]消耗战(虚树/树形DP)

    Time Limit: 20 Sec  Memory Limit: 512 MB
    Submit: 5246  Solved: 1978
    [Submit][Status][Discuss]

    Description

    在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
    侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

    Input

    第一行一个整数n,代表岛屿数量。

    接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。

    第n+1行,一个整数m,代表敌方机器能使用的次数。

    接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

    Output

    输出有m行,分别代表每次任务的最小代价。

     

    Sample Input

    10
    1 5 13
    1 9 6
    2 1 19
    2 4 8
    2 3 91
    5 6 8
    7 5 4
    7 8 31
    10 7 9
    3
    2 10 6
    4 5 7 8 3
    3 9 4 6

    Sample Output

    12
    32
    22

    HINT

     对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

    Source

    感觉虚树还算比较好理解吧。

    首先考虑最暴力的dp,

    设$f[x]$表示处理完以$x$为根的子树的最小花费

    转移有两种情况

    1.断开自己与父亲的联系,代价为从根到该节点的最小值

    2.将子树内的节点全都处理掉的代价

    但这样时间复杂度是$O(nm)$的,显然过不了

    虚树就是把有用的节点都拿出来。这里有用的节点指的是询问节点和他们的lca

    然后每次DP的时候只在虚树上DP就可以了

    这样时间复杂度为$2*sum_k$

    更多关于虚树的姿势可以去这里https://www.cnblogs.com/SovietPower/p/9142068.html

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
    #define LL long long
    char buf[(1 << 21) + 1], *p1 = buf, *p2 = buf;
    using namespace std;
    const int MAXN = 250001;
    inline int read() {
        char c = getchar(); int x = 0, f = 1;
        while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    char obuf[1 << 24], *O=obuf;
    void print(LL x) {
        if(x > 9) print(x / 10);
        *O++= x % 10 + '0';
    }
    int N, M;
    struct Edge {
        int u, v, w, nxt;
    }E[MAXN << 1];
    int head[MAXN], num = 1;
    inline void AddEdge(int x, int y, int z) {
        E[num] = (Edge) {x, y, z, head[x]};
        head[x] = num++;
    }
    vector<int> v[MAXN];
    void add_edge(int x, int y) {
        v[x].push_back(y);
    }
    int a[MAXN], dfn[MAXN], topf[MAXN], siz[MAXN], son[MAXN], s[MAXN], top, deep[MAXN], fa[MAXN], ID = 0;
    LL mn[MAXN];
    void dfs1(int x, int _fa) {
        siz[x] = 1; fa[x] = _fa;
        for(int i = head[x]; i != -1; i = E[i].nxt) {
            if(E[i].v == _fa) continue;
            deep[E[i].v] = deep[x] + 1;
            mn[E[i].v] = min(mn[x], (LL)E[i].w);
            dfs1(E[i].v, x);
            siz[x] += siz[E[i].v];
            if(siz[E[i].v] > son[x]) son[x] = E[i].v;
        }
    }
    void dfs2(int x, int topfa) {
        topf[x] = topfa;
        dfn[x] = ++ID;
        if(!son[x]) return ;
        dfs2(son[x], topfa);
        for(int i = head[x]; i != -1; i = E[i].nxt) 
            if(!topf[E[i].v]) 
                dfs2(E[i].v, E[i].v);
    }
    int LCA(int x, int y) {
        while(topf[x] != topf[y]) {
            if(deep[topf[x]] < deep[topf[y]]) swap(x, y);
            x = fa[topf[x]];
        }
        if(deep[x] < deep[y]) swap(x, y);
        return y;
    }
    void insert(int x) {
        if(top == 1) {s[++top] = x; return ;}
        int lca = LCA(x, s[top]);
        if(lca == s[top]) return ;
        while(top > 1 && dfn[s[top - 1]] >= dfn[lca]) add_edge(s[top - 1], s[top]), top--;
        if(lca != s[top]) add_edge(lca, s[top]), s[top] = lca;//
        s[++top] = x;
    }
    LL DP(int x) {
        if(v[x].size() == 0) return mn[x];
        LL sum = 0;
        for(int i = 0; i < v[x].size(); i++) 
            sum += DP(v[x][i]);
        v[x].clear();
        return min(sum, (LL)mn[x]);
    }
    int comp(const int &a, const int &b) {
        return dfn[a] < dfn[b];
    }
    int main() {
        #ifdef WIN32
        freopen("a.in", "r", stdin);
        #endif
        memset(head, -1, sizeof(head));
        mn[1] = 1ll << 60;
        N = read();
        for(int i = 1; i <= N - 1; i++) {
            int x = read(), y = read(), z = read();
            AddEdge(x, y, z); AddEdge(y, x, z);
        }
        deep[1] = 1;
        dfs1(1, 0);
        dfs2(1, 1);
        M = read();
        while(M--) {
            int K = read();
            for(int i = 1; i <= K; i++) a[i] = read();
            sort(a + 1, a + K + 1, comp);
            s[top = 1] = 1;
            for(int i = 1; i <= K; i++) insert(a[i]);
            while(top > 0)  add_edge(s[top - 1], s[top]), top--;
            print(DP(1)), *O++ = '
    '; 
        }
        fwrite(obuf, O-obuf, 1 , stdout);    
        return 0;
    }
  • 相关阅读:
    HDU 5912 Fraction (模拟)
    CodeForces 722C Destroying Array (并查集)
    CodeForces 722B Verse Pattern (水题)
    CodeForces 722A Broken Clock (水题)
    CodeForces 723D Lakes in Berland (dfs搜索)
    CodeForces 723C Polycarp at the Radio (题意题+暴力)
    CodeForces 723B Text Document Analysis (水题模拟)
    CodeForces 723A The New Year: Meeting Friends (水题)
    hdu 1258
    hdu 2266 dfs+1258
  • 原文地址:https://www.cnblogs.com/zwfymqz/p/9175093.html
Copyright © 2011-2022 走看看