SDOI 2011 消耗战 luoguP2495 树形dp

版权声明:_ https://blog.csdn.net/lunch__/article/details/82848053

题意

  • 给你一棵有边权的树,每次询问一些点,求断开这些点与根节点联系的最小代价

先考虑只有一次询问的情况

f [ i ] f[i] 为断开 f [ i ] f[i] 及其子树内所有询问点的最小代价

m n [ i ] mn[i] i i 号点到根节点路径上的最小边权

那么 f [ i ] = m i n ( s o n i f [ s o n i ] , m n [ i ] ) f[i] = min(\sum_{son_i}f[son_i], mn[i])

初始化询问点的 f [ q ] = m n [ q ] f[q]=mn[q]

这样做是 O ( n m ) O(nm) 40 p t s   g e t 40pts\ get

我们发现这样访问了很多不必要的节点

那么我们可不可以跨过他们呢?\sum{k}

答案当然是 y e s yes ,我们思考一下 d f s dfs 的过程

实际上就是入栈和出栈的过程

那么我们如果知道每个点入栈和出栈的时间

不就可以用栈模拟 d f s dfs 了吗?

那么流程如下 我们首先求出所有点的入栈出栈的时间戳

每次提取出询问点和时间戳相邻的两个点的 l c a lca

对于这些点按照入栈时间和出栈时间排序模拟 d f s dfs

进行树形 d p dp 就可以了,复杂度 O ( k log k ) O(\sum{k}\log\sum{k})

Codes

#include<bits/stdc++.h>
#include<bits/extc++.h>

#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define For(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
#define FOR(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)
#define debug(x) cout << #x << " = " << x << endl
#define mem(a, b) memset(a, b, sizeof(a))
#define cpy(a, b) memcpy(a, b, sizeof(a))
#define min(a, b) (a < b ? a : b)
#define max(a, b) (b < a ? a : b)
#define inf (0x3f3f3f3f)
#define INF (1e18)
#define pb push_back
#define mp make_pair
#define x first
#define y second

typedef unsigned long long ull;
typedef unsigned int uint;
typedef long long ll;
typedef std::pair<ll, int> PLI;
typedef std::pair<int, int> PII;
typedef double db;

namespace IO {
#define getc() ((S_ == T_) && (T_ = (S_ = Ch_) + fread(Ch_, 1, Buffsize, stdin), S_ == T_) ? 0 : *S_ ++)
    const uint Buffsize = 1 << 15, Output = 1 << 23;
    static char Ch_[Buffsize], *S_ = Ch_, *T_ = Ch_;
    static char Out[Output], *nowps = Out;
    inline void flush(){fwrite(Out, 1, nowps - Out, stdout); nowps = Out;}
    template<typename T>inline void read(T &_) {
        _ = 0; static char __; T ___ = 1;
        for(__ = getc(); !isdigit(__); __=getc()) if(__ == '-') ___ = -1;
        for(; isdigit(__); __ = getc()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
        _ *= ___;
    }
    template<typename T>inline void write(T _, char __ = '\n') {
        if(!_) *nowps ++ = '0';
        if(_ < 0) *nowps ++ = '-', _ = -_;
        static uint sta[111], tp;
        for(tp = 0; _; _ /= 10) sta[++ tp] = _ % 10;
        for(; tp; *nowps ++ = sta[tp --] ^ 48); *nowps ++ = __;
    }
    template<class T>inline bool chkmax(T &_, T __) {return _ < __ ? _ = __, 1 : 0;}
    template<class T>inline bool chkmin(T &_, T __) {return _ > __ ? _ = __, 1 : 0;}
}

using namespace std;
using namespace IO;

const int N = 500000 + 10;

int head[N], nxt[N << 1], v[N << 1], to[N << 1], e;
int idin[N], idout[N], dfn_cnt, rel[N];
int fa[N][20], dep[N], vis[N], tmp[N];
int n, q, cnt;

stack<int> S; int ins[N];

ll res[N], f[N], mn[N];

inline void add(int x, int y, int z) {
    to[++ e] = y; nxt[e] = head[x]; head[x] = e; v[e] = z;
}

inline void dfs(int x) {
    idin[x] = ++ dfn_cnt; rel[dfn_cnt] = x;
    For(j, 1, 19) fa[x][j] = fa[fa[x][j - 1]][j - 1];
    go(x, i) if(to[i] != fa[x][0]) {
        dep[to[i]] = dep[fa[to[i]][0] = x] + 1;
        chkmin(mn[to[i]], min(v[i], mn[x])); dfs(to[i]);
    }
    idout[x] = ++ dfn_cnt; rel[dfn_cnt] = x;
}

inline int lca(int x, int y) {
    if(dep[x] != dep[y]) {
        if(dep[x] < dep[y]) swap(x, y);
        FOR(j, 19, 0) x = dep[fa[x][j]] > dep[y] ? fa[x][j] : x;
        x = fa[x][0];
    }
    if(x == y) return x;
    FOR(j, 19, 0) if(fa[x][j] != fa[y][j]) x = fa[x][j], y = fa[y][j];
    return fa[x][0];
}

int main() {
#ifdef ylsakioi
    file("2495");
#endif
    int x, y, z; read(n);
    For(i, 2, n) read(x), read(y), read(z), add(x, y, z), add(y, x, z);
    mem(mn, inf); dfs(dep[1] = 1);
    for(read(q); q -- ; cnt = 0) {
        read(x); tmp[++ cnt] = 1, f[1] = mn[1];
        For(i, 1, x) read(y), tmp[++ cnt] = idin[y], f[y] = mn[y], vis[y] = 1;
        sort(tmp + 1, tmp + cnt + 1);
        For(i, 2, cnt) {
            int LCA = lca(rel[tmp[i - 1]], rel[tmp[i]]);
            if(!f[LCA]) tmp[++ cnt] = idin[LCA], f[LCA] = mn[LCA];
        }
        For(i, 1, cnt) tmp[++ cnt] = idout[rel[tmp[i]]];
        sort(tmp + 1, tmp + cnt + 1);
        For(i, 1, cnt) {
            if(!ins[rel[tmp[i]]]) S.push(ins[rel[tmp[i]]] = rel[tmp[i]]);
            else {
                int now = S.top(); S.pop(); ins[now] = 0; 
                if(!vis[now]) chkmin(f[now], res[now]);
                if(!S.empty()) res[S.top()] += f[now];
            }
        }
        write(f[1]); For(i, 1, cnt) res[rel[tmp[i]]] = vis[rel[tmp[i]]] = f[rel[tmp[i]]] = 0;
    }
    return flush(), 0;
}


猜你喜欢

转载自blog.csdn.net/lunch__/article/details/82848053
今日推荐