bzoj2286 [Sdoi2011]消耗战
2286: [Sdoi2011]消耗战
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4797 Solved: 1766
[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
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
分析:学习了一波虚树.
关于虚树在sengxian的博客中有较为详细的介绍:传送门. 主要想说一下这道题的注意点.
1.因为是阻断1号点到特殊点的路径,所以如果有特殊点在同一条链上,保留上面的那一个就好了.
2.连边可能会出现相同的点,要排除掉.
3.注意一开始要将1号点加入栈中.
4.最后的dp方程很简单:f[i] = min{Σf[j],f[i]}.f[i]初始化为i这个点到1号点路径的最短边长度. 这个dp方程是什么意思呢?要么把i号点连向儿子的边全都砍掉,要么砍掉祖先的边.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll maxn = 250010; const ll inf = 1e17; ll n,head1[maxn],to1[maxn * 2],nextt1[maxn * 2],tot1 = 1,w1[maxn * 2],m; ll head2[maxn],to2[maxn * 2],nextt2[maxn * 2],tot2 = 1,w2[maxn * 2]; ll deep[maxn],fa[maxn][20],pos[maxn],dfs_clock,q[maxn],k,tot,sta[maxn],top; ll f[maxn],d[maxn]; void add1(ll x,ll y,ll z) { w1[tot1] = z; to1[tot1] = y; nextt1[tot1] = head1[x]; head1[x] = tot1++; } void add2(ll x,ll y) { if (x == y) return; to2[tot2] = y; nextt2[tot2] = head2[x]; head2[x] = tot2++; } ll lca(ll x,ll y) { if (deep[x] < deep[y]) swap(x,y); for (ll i = 19; i >= 0; i--) if (deep[fa[x][i]] >= deep[y]) x = fa[x][i]; if (x == y) return x; for (ll i = 19; i >= 0; i--) if (fa[x][i] != fa[y][i]) { x = fa[x][i]; y = fa[y][i]; } return fa[x][0]; } void dfs(ll u,ll faa) { fa[u][0] = faa; pos[u] = ++dfs_clock; for (int i = 1; i <= 19; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1]; for (ll i = head1[u]; i; i = nextt1[i]) { ll v = to1[i]; if (v == faa) continue; d[v] = min(d[u],w1[i]); deep[v] = deep[u] + 1; dfs(v,u); } } bool cmp(ll x,ll y) { return pos[x] < pos[y]; } void dp(ll u) { ll res = 0; f[u] = d[u]; for (ll i = head2[u]; i; i = nextt2[i]) { ll v = to2[i]; dp(v); res += f[v]; } head2[u] = 0; if (!res) f[u] = d[u]; else if (res < f[u]) f[u] = res; } void solve() { scanf("%lld",&k); for (ll i = 1; i <= k; i++) scanf("%lld",&q[i]); sort(q + 1,q + 1 + k,cmp); tot = 0; q[++tot] = q[1]; for (ll i = 2; i <= k; i++) if (lca(q[tot],q[i]) != q[tot]) q[++tot] = q[i]; top = 0; tot2 = 1; sta[++top] = 1; for (ll i = 1; i <= tot; i++) { ll LCA = lca(q[i],sta[top]);while (1) { if (deep[sta[top - 1]] <= deep[LCA]) { add2(LCA,sta[top]); top--; if (sta[top] != LCA) sta[++top] = LCA; break; } add2(sta[top - 1],sta[top]); top--; } if (sta[top] != q[i]) sta[++top] = q[i]; } top--; while (top) { add2(sta[top],sta[top + 1]); top--; } dp(1); printf("%lld\n",f[1]); } int main() { scanf("%lld",&n); for (ll i = 1; i < n; i++) { ll u,v,w; scanf("%lld%lld%lld",&u,&v,&w); add1(u,v,w); add1(v,u,w); } d[1] = inf; deep[1] = 1; dfs(1,0); scanf("%lld",&m); while (m--) solve(); return 0; }