【BZOJ2286】【SDOI2011】消耗战(虚树,树形DP)

本文介绍了一种解决战争中防止敌方获取能源问题的算法。通过构建虚树并运用树形动态规划方法,确保在最小代价下破坏关键路径,阻止敌军获取能源。考虑到敌方可能利用特殊设备修复被破坏的设施并重新分配资源,该算法能够灵活应对多次变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。

侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。


Solution

算是很基础的虚树题了吧,都不要存边权。

对于每次询问建出虚树,然后树形DP即可。
我们建虚树之前可以对点进行处理:如果一个是另一个的祖先,那么将深度较大的那个点删去。这样可以方便DP。


Source

/************************************************
 * Au: Hany01
 * Date: Apr 1st, 2018
 * Prob: [BZOJ2286][SDOI2011] 消耗战
 * Email: hany01@foxmail.com
************************************************/

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define ALL(a) (a).begin(), (a).end()
#define SZ(a) ((int)(a).size())
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define Mod (1000000007)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define y1 wozenmezhemecaia
#define oo (1000000000000000000ll)

template <typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }

inline int read()
{
    register int _, __; register char c_;
    for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
    for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
    return _ * __;
}

const int maxn = 250005;

int las[maxn], e, v[maxn << 1], w[maxn << 1], nex[maxn << 1], beg[maxn], dfn[maxn], dep[maxn], fa[maxn][19], tim;
LL dp[maxn], val[maxn];
vector<int> Next[maxn];

inline void add(int uu, int vv, int ww) { v[++ e] = vv, w[e] = ww, nex[e] = beg[uu], beg[uu] = e; }

inline void ADD(int u, int v, int id)
{
    if (u == v) return ;
    if (las[u] != id) las[u] = id, Next[u].clear();
    if (las[v] != id) las[v] = id, Next[v].clear();
    Next[u].pb(v);
}

void dfs(int u, int pa)
{
    dfn[u] = ++ tim, dep[u] = dep[pa] + 1, fa[u][0] = pa;
    for (register int i = beg[u]; i; i = nex[i]) if (v[i] != pa)
        val[v[i]] = min(val[u], (LL)w[i]), dfs(v[i], u);
}

inline int LCA(int u, int v)
{
    if (dep[u] < dep[v]) swap(u, v);
    Fordown(i, 17, 0)
        if (dep[u] - (1 << i) >= dep[v]) u = fa[u][i];
    if (u == v) return u;
    Fordown(i, 17, 0) if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
    return fa[u][0];
}

inline bool cmp(const int& A, const int& B) { return dfn[A] < dfn[B]; }

void DP(int u)
{
    register LL tmp = 0;
    register int sz = SZ(Next[u]);
    dp[u] = val[u];
    if (!sz) return ;
    rep(i, sz) DP(Next[u][i]), tmp += dp[Next[u][i]];
    chkmin(dp[u], tmp);
}

int main()
{
#ifdef hany01
    File("bzoj2286");
#endif

    //Init
    static int n, uu, vv, ww, s[maxn], stk[maxn];
    n = read();
    For(i, 2, n) uu = read(), vv = read(), ww = read(), add(uu, vv, ww), add(vv, uu, ww);
    val[1] = oo, dfs(1, 0);
    for (int j = 1; (1 << j) <= n; ++ j)
        For(i, 1, n) fa[i][j] = fa[fa[i][j - 1]][j - 1];

    //Solve
    for (static int m = read(); m --; )
    {
        register int tot = read(), top, cnt = 1;
        For(i, 1, tot) s[i] = read();
        sort(s + 1, s + 1 + tot, cmp);
        For(i, 2, tot) if (LCA(s[cnt], s[i]) != s[cnt]) s[++ cnt] = s[i];
        tot = cnt;
        stk[top = 1] = 1;
        For(i, 1, tot)
        {
            register int u = s[i], lca = LCA(u, stk[top]);
            if (lca == stk[top]) stk[++ top] = u;
            else {
                while (top >= 2 && dep[stk[top - 1]] > dep[lca])
                    ADD(stk[top - 1], stk[top], m + 1), -- top;
                if (stk[top] != lca) ADD(lca, stk[top], m + 1), stk[top] = lca;
                stk[++ top] = u;
            }
        }
        For(i, 1, top - 1) ADD(stk[i], stk[i + 1], m + 1);
        DP(1);
        printf("%lld\n", dp[1]);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值