2286: [Sdoi2011]消耗战
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 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
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
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
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;
}