「洛谷2495」「BZOJ3052」「SDOI2001」消耗战【虚树+树形动态规划】

题目大意

给你\(k\)个点,让这一些点和一号节点断开,删去某一些边,求最小的删去边权之和。

做题的心路历程

做了\(HG\)昨天的模拟赛,深深感觉到了窝的菜,所以为了\(A\)掉T1这一道毒瘤,窝就来学习一下虚树。
学到一半,感觉虚树的原理还是比较简单的,就是把需要求的点建一棵和原来的树长得十分相似的一棵树,然后在这个树上做\(DP\)
但是,我写到完了,就一直T。也不知道为什么。
然后删掉了所有的memset就瞬间跑了出来。原来是因为自己太怂了。。。

题解

首先如果考虑最简单的树形\(DP\)
\(f[i]\)表示以\(i\)为根节点的答案。
那么我们需要预处理出每一个节点到根节点\(1\)的最小边权设为\(me[i]\),那么就得到了\(f[i]=\sum \ min(f[i],me[i])\)
但是发现,如果每一个询问都做一遍树形\(DP\),那么一定是要炸的。
那么就对于我们需要询问的节点建立一个树,然后在这个树上跑DP。

代码

#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define N 550005
#define ms(a, b) memset(a, b, sizeof(a))
#define ll long long 
#define BIT 19
using namespace std;
template <typename T> void read(T &x) {
    x = 0; T fl = 1; char ch = 0;
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') fl = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    x *= fl;
}
struct edge { int to, nt, w; } E[N << 1];
vector<int> G[N];
int H[N], dfn[N], ispoint[N], f[N][BIT + 5], dep[N], p[N], stk[N];
ll me[N];
int cnt, __dfn, n, Q, k, top;
void add_edge(int u, int v, int w) { E[++ cnt] = (edge){v, H[u], w}; H[u] = cnt; }
bool cmp(int A, int B) { return dfn[A] < dfn[B]; }
void dfs1(int u, int fa) {
    f[u][0] = fa; dfn[u] = ++ __dfn; dep[u] = dep[fa] + 1;  
    for (int i = 1; i <= BIT; i ++) f[u][i] = f[f[u][i - 1]][i - 1];
    for (int e = H[u]; e; e = E[e].nt) { int v = E[e].to; if (v == fa) continue; me[v] = E[e].w; if (u != 1 && me[u] < me[v]) me[v] = me[u]; dfs1(v, u);  }
}
int lca(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    for (int i = BIT; ~i; i --) 
        if (dep[f[u][i]] >= dep[v]) u = f[u][i]; 
    if (u == v) return u;
    for (int i = BIT; ~i; i --) 
        if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
    return f[u][0];
}
void insert(int u) {
    if (top == 1) { stk[++ top] = u; return; }
    int Lca = lca(u, stk[top]);
    if (Lca == stk[top]) { stk[++ top] = u; return; }
    while (top > 1 && dfn[Lca] <= dfn[stk[top - 1]]) G[stk[top - 1]].push_back(stk[top]), -- top;
    if (Lca != stk[top]) G[Lca].push_back(stk[top]), stk[top] = Lca; 
    stk[++ top] = u; 
}
ll DP(int u) {
    ll res = 0;
    for (int i = 0; i < (int) G[u].size(); i ++) {
        int v = G[u][i]; res += min(me[v], DP(v));
    }
    G[u].clear(); 
    if (ispoint[u]) return me[u]; else return res; 
}
int main() {
    read(n);  
    for (int i = 1, u, v, w; i < n; i ++) { read(u); read(v); read(w); add_edge(u, v, w); add_edge(v, u, w);  }
    __dfn = 0; dfs1(1, 0);
    read(Q);
    while (Q --) {
        read(k);  for (int i = 1; i <= k; i ++) read(p[i]), ispoint[p[i]] = 1; 
        sort(p + 1, p + 1 + k, cmp);  top = 0; stk[++ top] = 1; 
        for (int i = 1; i <= k; i ++) insert(p[i]);
        while (top > 0) { G[stk[top - 1]].push_back(stk[top]); top --; } 
        printf("%lld\n", DP(1));
        for (int i = 1; i <= k; i ++) G[p[i]].clear(), ispoint[p[i]] = 0; G[0].clear(); 
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/chhokmah/p/10695437.html