【 bzoj 2286 】 : [Sdoi2011]消耗战 - 树形DP

  这道题的思路感觉挺赞的……
  暴力的DP,f[i]表示切到i点的最小代价,显然有 f[u]=min{vip[v]?inf:f[v],dis(u,v)}
  这其中中间有很多dp都是不必要的,因为如果一条链D下来,那么两个关键点之间更新的权值都是相邻两点的距离,可以省略掉。
  然后就可以用一个应该是挺经典的做法:用单调栈维护一条链。
  这样思路就很明显了,先按dfs序排序,然后用单调栈维护一条当前DP的链,每当加入一个新的关键点的时候就看看它是不是接在了链上,不然就一直往上跑也就是弹栈,直到接到了链上,在弹的过程中DP就可以了。
  感觉这个做法可以用在很多跟删除连接关键点的边有关的题目里面。
  

#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i = a , _ = b ; i <= _ ; i ++)
#define per(i,a,b) for(int i = a , _ = b ; i >= _ ; i --)
#define fore(i,u) for(int i = head[u] ; i ; i = nxt[i] )
#define maxn 250007
#define maxm 500007

inline int rd() {
    char c = getchar();
    while (!isdigit(c)) c = getchar() ; int x = c - '0';
    while (isdigit(c = getchar())) x = x * 10 + c - '0';
    return x;
}

template<class T> inline void upmin(T&a , T b) { if (a > b) a = b ; }

typedef long long ll;

const ll inf = 1ll << 60 ;

typedef int arr_int[maxn];
typedef ll  arr_ll [maxn];
typedef int adj[maxm];

arr_int head , pos , dep , h , st , g;
arr_ll  f;
adj to , nxt , val ;

int dis[maxn][19];

int fa[maxn][19];

int n , ett , dfs_clock , top;


inline void ins(int u , int v , int w) {
    to[++ ett] = v , val[ett] = w , nxt[ett] = head[u] , head[u] = ett;
}

void dfs(int u) {
    pos[u] = ++ dfs_clock;
    rep (i , 1 , 18) fa[u][i] = fa[fa[u][i - 1]][i - 1] , dis[u][i] = min(dis[fa[u][i - 1]][i - 1] , dis[u][i - 1]);
    fore (i , u) {
        int v = to[i] , w = val[i];
        if (v == fa[u][0]) continue;
        dep[v] = dep[u] + 1 , fa[v][0] = u , dis[v][0] = w;
        dfs(v);
    }
}

inline int lca(int u , int v) {
    if (dep[u] < dep[v]) swap(u , v);
    int d = dep[u] - dep[v];
    rep (i , 0 , 18) if ((1 << i) & d) u = fa[u][i];
    if (u == v) return u;
    per (i , 18 , 0) if (fa[u][i] != fa[v][i]) u = fa[u][i] , v = fa[v][i];
    return fa[u][0];
}

inline ll  Dis(int u , int v) {
    if (dep[u] < dep[v]) swap(u , v);
    int d = dep[u] - dep[v];
    int ret = 0x7fffffff;
    rep (i , 0 , 18) if ((1 << i) & d) upmin(ret , dis[u][i]) , u = fa[u][i];
    if (u == v) return ret;
    per (i , 18 , 0) if (fa[u][i] != fa[v][i]) upmin(ret , min(dis[u][i] , dis[v][i])) , u = fa[u][i] , v = fa[v][i];
    upmin(ret , min(dis[u][0] , dis[v][0]));
    return (ll) ret;
}

void input() {
    n = rd();
    rep (i , 2 , n) {
        int u = rd() , v = rd() , w = rd();
        ins(u , v , w) , ins(v , u , w);
    }
    dep[1] = 1 , dfs(1);
}

void work(int k) {
    st[top = 1] = 1;
    f[top] = g[top] = 0;
    rep (i , 1 , k) {
        int p = lca(st[top] , h[i]);
        while (dep[st[top]] > dep[p]) {
            if (dep[st[top - 1]] <= dep[p]) {
                ll tmp = min(g[top] ? inf : f[top] , Dis(p , st[top]));
                st[top --] = 0;
                if (st[top] != p) {
                    st[++ top] = p;
                    f[top] = 0 , g[top] = 0;
                }
                f[top] += tmp;
                break;
            } else {
                f[top - 1] += min(g[top] ? inf : f[top] , Dis(st[top - 1] , st[top]));
                st[top --] = 0;
            }
        }
        if (st[top] != h[i]) {
            st[++ top] = h[i];
            f[top] = 0;
        }
        g[top] = 1;
    }
    for (;top > 1; st[top --] = 0)
        f[top - 1] += min(g[top] ? inf : f[top] , Dis(st[top - 1] , st[top]));
    printf("%lld\n" , f[1]);
}

bool cmp(const int a , const int b) {
    return pos[a] < pos[b];
}

void solve() {
    rep (m , 1 , rd()) {
        int k = rd();
        rep (i , 1 , k) h[i] = rd();
        sort(h + 1 , h + k + 1 , cmp);
        work(k);
    }
}

int main() {
    #ifndef ONLINE_JUDGE
        freopen("data.txt" , "r" , stdin);
    #endif
    input();
    solve();
    return 0;
}

你可能感兴趣的:(【 bzoj 2286 】 : [Sdoi2011]消耗战 - 树形DP)