消耗战 BZOJ 2286
消耗战
【问题描述】
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。
【输入格式】
第一行一个整数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,表示资源丰富岛屿的编号。
【输出格式】
输出有m行,分别代表每次任务的最小代价。
【样例输入】
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
【样例输出】
12
32
22
【数据规模和约定】
对于10%的数据,2<=n<=10,1<=m<=5,1<=ki<=n-1
对于20%的数据,2<=n<=100,1<=m<=100,1<=ki<=min(10,n-1)
对于40%的数据,2<=n<=1000,m>=1,sigma(ki)<=500000,1<=ki<=min(15,n-1)
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
题解:
题目就是给定一棵树,询问删去一些边使输入关键节点与根节点1不连通的最小费用
虚树就是保留只所有关键节点和它们的公共祖先的连边所构建出的树,每次询问O(mlog2m)
我们记val[i]为使根节点与i不连通的最小花费
ti[i]表示i的Dfs序
对于每次询问
将所有关键节点按ti排序(根节点自然也算关键节点)
将相邻关键节点的最近公共祖先加入数组,这样就可以将所有关键节点的公共祖先
小小证明一下能够找出所有公共祖先:
我们假定按Dfs序排好的三个点A,B,C
如果其中两个或三个在同一个子树中,那么显然能够找出所有公共祖先
如果都不在同一棵子树上,那么设A与B的公共祖先为D,B与C的公共祖先为E
接下来A到根节点的链简称A链,其它同理
因为它们按Dfs序处理过,公共祖先必定在根节点分别与两点组成的链上
由此可知D和E都在B链上
假设D在E的上方,那么D就是A与C的最近公共祖先
证明:
由于D在A链上,E在C链上
那么D链是A链与C链的共同部分
所以A和C的公共祖先在D链上
由于D链在B链上
所以在D链上离A最近的点是A与B的公共祖先即D
则D为A、C的公共祖先
E在D的上方同理
多个点同理
一小部分的证明自己脑补啦~~~也可以去看虚树讲义
草率地证明完毕
证明有毒但是代码巨短
再次排序,去重
接下来用单调栈连接所有关键节点之间的边
大概讲一下单调栈:
我们已经将点按Dfs序排序过了
所以那么我们就可以直接判断栈顶元素与当前元素的关系,即当前元素是否在栈顶元素的子树中,同时满足能顺序访问每个点的子树
如果在子树中,那么连边,权值为当前节点的val
如果不在,那么说明当前元素在栈顶元素子树的外边,退栈,继续判断两者关系
这样就够出了一个虚树
接下来再来一个简单的树形Dp就好了
~\(≧▽≦)/~啦啦啦
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdlib>
5 #include<cstdio>
6 #include<cmath>
7 using namespace std;
8 inline int Get()
9 {
10 int x = 0;
11 char c = getchar();
12 while('0' > c || c > '9') c = getchar();
13 while('0' <= c && c <= '9')
14 {
15 x = (x << 3) + (x << 1) + c - '0';
16 c = getchar();
17 }
18 return x;
19 }
20 const int me = 2000233;
21 struct shape
22 {
23 int lca, mi;
24 };
25 int n;
26 int m;
27 int k;
28 int ns, s[me];
29 int tot, to[me], nex[me], fir[me];
30 long long va[me];
31 inline void Ins(int x, int y, int z)
32 {
33 nex[++tot] = fir[x];
34 fir[x] = tot;
35 to[tot] = y;
36 va[tot] = z;
37 }
38 int de[me];
39 int si[me];
40 int fa[me][23];
41 int nt, ti[me];
42 long long val[me];
43 bool mark[me];
44 void Dfs(int u, int fat)
45 {
46 si[u] = 1;
47 de[u] = de[fat] + 1;
48 ti[u] = ++nt;
49 for(int i = fir[u]; i; i = nex[i])
50 {
51 int v = to[i];
52 if(v == fat) continue;
53 fa[v][0] = u;
54 val[v] = min(val[u], va[i]);
55 Dfs(v, u);
56 si[u] += si[v];
57 }
58 }
59 inline bool rt(int x, int y)
60 {
61 return ti[x] < ti[y];
62 }
63 inline int Lca(int x, int y)
64 {
65 if(de[x] < de[y]) swap(x, y);
66 for(int i = k; de[x] != de[y], i >= 0; --i)
67 if(de[fa[x][i]] >= de[y])
68 x = fa[x][i];
69 for(int i = k; i >= 0; --i)
70 if(fa[x][i] != fa[y][i])
71 x = fa[x][i], y = fa[y][i];
72 if(x != y) return fa[x][0];
73 return x;
74 }
75 long long Dp(int u, int fat)
76 {
77 if(mark[u] || !fir[u]) return val[u];
78 long long sum = 0;
79 for(int i = fir[u]; i; i = nex[i])
80 {
81 int v = to[i];
82 if(v == fat) continue;
83 sum += min(Dp(v, u), val[v]);
84 }
85 return sum;
86 }
87 void Clear(int u, int fat)
88 {
89 for(int i = fir[u]; i; i = nex[i])
90 {
91 int v = to[i];
92 if(v == fat) continue;
93 Clear(v, u);
94 }
95 fir[u] = 0;
96 }
97 int top, st[me];
98 int main()
99 {
100 n = Get();
101 k = log(n) + 1;
102 for(int i = 1; i < n; ++i)
103 {
104 int u = Get();
105 int v = Get();
106 int z = Get();
107 Ins(u, v, z);
108 Ins(v, u, z);
109 }
110 for(int i = 0; i <= n; ++i) val[i] = 214748364721474836LL;
111 Dfs(1, 0);
112 for(int j = 1; j <= k; ++j)
113 for(int i = 1; i <= n; ++i)
114 fa[i][j] = fa[fa[i][j - 1]][j - 1];
115 for(int i = 1; i <= n; ++i) fir[i] = 0;
116 tot = 0;
117 m = Get();
118 for(int i = 1; i <= m; ++i)
119 {
120 int p = Get();
121 for(int i = 1; i <= p; ++i)
122 {
123 s[i] = Get();
124 mark[s[i]] = true;
125 }
126 sort(s + 1, s + 1 + p, rt);
127 ns = p;
128 s[++ns] = 1;
129 for(int i = 1; i < p; ++i)
130 s[++ns] = Lca(s[i], s[i + 1]);
131 sort(s + 1, s + 1 + ns, rt);
132 int lens = 0;
133 for(int i = 1; i <= ns; ++i)
134 if(s[i] != s[i - 1])
135 s[++lens] = s[i];
136 top = 0;
137 for(int i = 1; i <= lens; ++i)
138 {
139 while(top > 0 && ti[s[i]] >= ti[st[top]] + si[st[top]]) --top;
140 if(top > 0) Ins(st[top], s[i], val[s[i]]);
141 st[++top] = s[i];
142 }
143 printf("%lld\n", Dp(1, 0));
144 for(int i = 1; i <= lens; ++i) mark[s[i]] = false;
145 tot = 0;
146 Clear(1, 0);
147 }
148 }