bzoj2286消耗战 虚树+树型动规

2286: [Sdoi2011]消耗战

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 3772  Solved: 1357
[ Submit][ Status][ Discuss]

Description

在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

Input

第一行一个整数n,代表岛屿数量。

接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。

第n+1行,一个整数m,代表敌方机器能使用的次数。

接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

Output

输出有m行,分别代表每次任务的最小代价。

 

Sample Input

10
1 5 13
1 9 6
2 1 19
2 4 8
2 3 91
5 6 8
7 5 4
7 8 31
10 7 9
3
2 10 6
4 5 7 8 3
3 9 4 6

Sample Output

12
32
22

HINT

 对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

Source

题目大意就是给一颗树,每次给一堆节点,然后去掉树上某几条边使得这些节点无法到根节点,在此基础上最小化边权和
一道虚树模板题,然后树型动规
虚树,大概就是把树中用到的关键节点不改变原来树的结构重新建一颗形式上的树,所有操作都在这棵树上进行就可以了,类似于缩图,以达到简化操作复杂度的目的。(敷衍~)
对于这题来说,只有选中的点需要建树。如果是裸的树dp 就是f[i] = min(mn[i], sum(mn[son[k]]))
mn[i]表示i到根节点路径中最小边权。f[i]表示使其子树都无法到达根的最小代价。也就是说,对于某个点i,要么它的所有子树到不了根,要么它到不了根。
然后就是虚树的操作,具体看 点击打开链接
然后每次建树,再dp就好啦
第一次调,调了一早上qwq(还是抄hzwer的)

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<cmath>
#include<set>
#define maxn 250005
#define inf 1e60
using namespace std;
int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
 
int pre[maxn][2], top;
struct edge {
    int to, next, w;
    void add(int u, int v, int ww, bool p)
    {
        if(u == v) return;
        to = v; next = pre[u][p];
        pre[u][p] = top++; w = ww;
    }
}e[maxn * 2], e2[maxn * 2];
int fa[maxn], deep[maxn], son[maxn], dson[maxn], d[maxn], in[maxn];
int h[maxn], st[maxn];
long long f[maxn], mn[maxn];
int n, m, tot, cnt;
void adds(int w, int v, int u) {
    e[top].add(u, v, w, 0);
    e[top].add(v, u, w, 0);
} 
bool cmp(int x, int y) {return in[x] < in[y];}
 
void dfs1(int u, int pa) {
    fa[u] = pa; deep[u] = deep[pa] + 1; son[u] = 1; dson[u] = 0;
    for(int i = pre[u][0]; i; i = e[i].next) {
        if(e[i].to == pa) continue;
        int v = e[i].to;
        mn[v] = min(mn[u], (long long)e[i].w);
        dfs1(v, u);
        son[u] += son[v];
        if(son[v] > son[dson[u]]) dson[u] = v;
    }
}
 
void dfs2(int u, int chain) {
    in[u] = ++tot; d[u] = chain;
    if(!dson[u]) return;
    dfs2(dson[u], chain);
    for(int i = pre[u][0]; i; i = e[i].next) 
        if(e[i].to != fa[u] && e[i].to != dson[u])
            dfs2(e[i].to, e[i].to);
}
 
int lca(int u, int v) {
    while(d[u] != d[v]) {
        if(deep[d[u]] < deep[d[v]]) swap(u, v);
        u = fa[d[u]];
    }
    if(in[u] > in[v]) swap(u, v);
    return u;
} 
 
void init() {
    n = read(); top = 1; mn[1] = inf;
    for(int i = 1;i < n; ++i) {
        adds(read(), read(), read());
    }
    dfs1(1, 0); dfs2(1, 1);
}
 
void dp(int u) {
    f[u] = mn[u];
    long long tmp = 0;
    for(int i = pre[u][1]; i; i = e2[i].next) {
        dp(e2[i].to);
        tmp += f[e2[i].to];
    }
    pre[u][1] = 0;
    if(tmp == 0) f[u] = mn[u];
    else if(tmp <= f[u]) f[u] = tmp;
}
 
void solve() {
    int K = read(); top = 1; tot = 0;
    for(int i = 1;i <= K; ++i) h[i] = read();
    sort(h + 1, h + K + 1, cmp);
    h[++tot] = h[1];
    for(int i = 2;i <= K; ++i) 
        if(lca(h[i], h[tot]) != h[tot]) h[++tot] = h[i];
    st[++cnt] = 1;
    for(int i = 1;i <= tot; ++i) {
        int cur = h[i], pa = lca(cur, st[cnt]);
        while(1) {
            if(deep[pa] >= deep[st[cnt - 1]]) {
                e2[top].add(pa, st[cnt--], 0, 1);
                if(st[cnt] != pa) st[++cnt] = pa;
                break;
            }
            e2[top].add(st[cnt - 1], st[cnt], 0, 1); --cnt;
        }
        if(st[cnt] != cur) st[++cnt] = cur;
    }
    while(--cnt) e2[top].add(st[cnt], st[cnt + 1], 0, 1);
    dp(1);
    printf("%lld\n", f[1]);
}
 
int main()
{
    init();
    m = read();
    for(int i = 1;i <= m; ++i) 
        solve();
    return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值