初二的时候就听老曹讲过这题。
据说一个最小割就过了。
最小割特别显然。
每个点x->x’连代价为1的边,不要这个点就是割掉这条边。
对于每个有人的点x,S->x连正无穷。
对于每个叶子节点x,x->T连正无穷。
对于每条树边x->y,x->y’连正无穷。
跑最大流=最小割,就是答案。
100000需要梦想。
正解是个辣鸡树形dp。
选一个叶子节点为根。
fi,0/1/2
分别表示:
0.子树中人不能到,没有到叶子的路径。
1.子树中人不能到,有路径。
2.子树中人能到,没有路径。
强行dp,什么情况都丢进去就好了。
所以我的方程写的很丑。
Code:
#include<cstdio>
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const int N = 1e5 + 5;
int n, m, x, y, r[N], bz[N];
int tot, next[N * 2], to[N * 2], final[N];
void link(int x, int y) {
next[++ tot] = final[x], to[tot] = y, final[x] = tot;
next[++ tot] = final[y], to[tot] = x, final[y] = tot;
}
int g, bx[N], f[N][3];
void dg(int x) {
bx[x] = 1;
if(r[x] == 1 && x != g) {
f[x][2] = n;
f[x][1] = 0;
f[x][0] = 1;
return;
}
if(bz[x]) f[x][2] = 0, f[x][0] = f[x][1] = n;
int m0 = 1, s = 0;
for(int i = final[x]; i; i = next[i]) {
int y = to[i]; if(bx[y]) continue;
dg(y);
if(bz[x]) {
f[x][2] += min(f[y][0], f[y][2]);
} else {
m0 += min(f[y][0], min(f[y][1], f[y][2]));
f[x][0] += f[y][0];
f[x][1] += min(f[y][0], f[y][1]);
f[x][2] += min(f[y][0], f[y][2]);
}
}
if(!bz[x]) f[x][0] = min(f[x][0], m0);
if(f[x][0] > n) f[x][0] = n;
if(f[x][1] > n) f[x][1] = n;
if(f[x][2] > n) f[x][2] = n;
}
int main() {
scanf("%d %d", &n, &m);
n ++;
fo(i, 1, n - 1) {
scanf("%d %d", &x, &y); x ++; y ++;
r[x] ++; r[y] ++;
link(x, y);
}
fo(i, 1, m) {
scanf("%d", &x); x ++;
bz[x] = 1;
if(r[x] == 1) {
printf("-1"); return 0;
}
}
g = 0;
fo(i, 1, n) if(r[i] == 1)
g = i;
dg(g);
f[g][2] ++;
printf("%d", min(f[g][0], min(f[g][1], f[g][2])));
}