zoukankan      html  css  js  c++  java
  • Luogu 2495 [SDOI2011]消耗战

    BZOJ 2286

    传说中的虚树经典题。

    放上我觉得讲的很好的Luogu置顶题解。       传送门

    首先考虑一个暴力的$dp$,设$f_x$表示切断$x$的子树中的所有特殊点且保留$x$的最小代价和,$mn_x$表示$x$到根的路径上最小的一条边的权值。

    边界:搜一遍预处理$mn$,对于所有的特殊点$x$, $f_x = mn_x$,其他的点$y$,$f_y = 0$。

    转移也比较显然$f_x = sum_{y} min(mn_y, f_y)$   $y in son(x)$。

    这样子$n^2$的做法就拿到了40分。

    我们在$dp$的时候发现其实一次要用的点很少,最多就是所有的特殊点,$1$,还有一些特殊点的$lca$,那么我们可以直接把这些需要的点保留下来重新构造一颗新的树来$dp$,这样子会快很多。

    好像这个东西就叫虚树吧。

    对于一棵树,我们只要知道了它的欧拉序就可以知道这棵树到底是长什么样的,发现构造出来的这颗新的树上所有点的相对欧拉序都没有变化,而且使用欧拉序还有一个好处,那就是在插入$lca$的时候只要先按照欧拉序排一波序然后按顺序插入就好了,感觉这比很多网上的用栈维护这个$lca$的方法要好很多。

    按照所有点的欧拉序排序后模拟系统栈$dp$即可。

    时间复杂度似乎是$O(nlogn)$,并不会算。

    另外注意每次在弹栈的时候初始化一下就好了,我一开始用$memset$T了。

    Code:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N = 2.5e5 + 5;
    const int Lg = 20;
    const ll inf = 1LL << 60;
    
    int n, qn, tot = 0, head[N], top = 0, sta[N << 2], a[N << 2];
    int fa[N][Lg], dep[N], dfsc = 0, in[N], out[N];
    ll mn[N], f[N];
    bool vis[N];
    
    struct Edge {
        int to, nxt;
        ll val;
    } e[N << 1];
    
    inline void add(int from, int to, ll val) {
        e[++tot].to = to;
        e[tot].val = val;
        e[tot].nxt = head[from];
        head[from] = tot;
    }
    
    template <typename T>
    inline void read(T &X) {
        X = 0; char ch = 0; T op = 1;
        for(; ch > '9' || ch < '0'; ch = getchar())
            if(ch == '-') op = -1;
        for(; ch >= '0' && ch <= '9'; ch = getchar())
            X = (X << 3) + (X << 1) + ch - 48;
        X *= op;
    }
    
    bool cmp(int x, int y) {
        int dfx = x > 0 ? in[x] : out[-x];
        int dfy = y > 0 ? in[y] : out[-y];
        return dfx < dfy;
    }
    
    inline ll min(ll x, ll y) {
        return x > y ? y : x;
    }
    
    void dfs(int x, int fat, int depth, ll nowMn) {
        fa[x][0] = fat, dep[x] = depth, mn[x] = nowMn;
        in[x] = ++dfsc;
        for(int i = 1; i <= 18; i++)
            fa[x][i] = fa[fa[x][i - 1]][i - 1];
        for(int i = head[x]; i; i = e[i].nxt) {
            int y = e[i].to;
            if(y == fat) continue;
            dfs(y, x, depth + 1, min(nowMn, e[i].val));
        }
        out[x] = ++dfsc;
    }
    
    inline void swap(int &x, int &y) {
        int t = x; x = y; y = t;
    }
    
    inline int getLca(int x, int y) {
        if(dep[x] < dep[y]) swap(x, y);
        for(int i = 18; i >= 0; i--)
            if(dep[fa[x][i]] >= dep[y])
                x = fa[x][i];
        if(x == y) return x;
        for(int i = 18; i >= 0; i--)
            if(fa[x][i] != fa[y][i])
                x = fa[x][i], y = fa[y][i];
        return fa[x][0];
    }
    
    void solve() {
        int K, cnt; read(K);
        for(int i = 1; i <= K; i++) {
            read(a[i]);
            vis[a[i]] = 1, f[a[i]] = mn[a[i]];
        }
        
        cnt = K;
        sort(a + 1, a + 1 + cnt, cmp);
        for(int i = 1; i < cnt; i++) {
            int now = getLca(a[i], a[i + 1]);
            if(!vis[now]) {
                vis[now] = 1;
                a[++cnt] = now;
            }
        }
        
        for(int cur = cnt, i = 1; i <= cur; i++)
            a[++cnt] = -a[i];
        if(!vis[1]) a[++cnt] = 1, a[++cnt] = -1;
         
        sort(a + 1, a + 1 + cnt, cmp);
        top = 0;
        for(int i = 1; i <= cnt; i++) {
            if(a[i] > 0) sta[++top] = a[i];
            else {
                int x = sta[top--];
                if(x == 1) printf("%lld
    ", f[x]);
                else {
                    int y = sta[top];
                    f[y] += min(mn[x], f[x]);            
                }
                f[x] = 0LL, vis[x] = 0;
            }
        }
    }
    
    int main() {
        read(n);
        for(int x, y, i = 1; i < n; i++) {
            read(x), read(y);
            ll v; read(v);
            add(x, y, v), add(y, x, v);
        }    
        
        dfs(1, 0, 1, inf);
        for(read(qn); qn--; ) solve();
        
        return 0;
    }
    View Code
  • 相关阅读:
    编译安装linux内核步骤
    怎样在github上协同开发
    在JSP里使用CKEditor和CKFinder
    tomcat结合nginx使用小结
    JAVA学习笔记——(五)
    JAVA学习笔记——(四)
    JAVA学习笔记——(三)
    JAVA学习笔记——(二)
    JAVA学习笔记——(一)
    ansible
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9698976.html
Copyright © 2011-2022 走看看