首先策略肯定是把Tom逼到一个点,然后去删边,接着取消标记让Tom一步步走到T那儿去。
Tom肯定会往代价最大的地方钻,1/4可以选择封住最大的先,然后Tom往次大的走。
以b为根,建树。
对于不在b->t的路径上的点,求出w,表示往它的子树中走一圈回来的最小代价,dp显然。
现在你可以想像从b->t走,你可以删掉相邻的子树,但是你不知道删哪棵。
于是二分答案,遵循能删必删的思想,就可以确定了,直到某个深度不行了。
Code:
#include
#include
#include
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;
const int N = 1e6 + 5;
int n, s, t, x, y, sum;
int final[N], next[N * 2], to[N * 2], tot;
int w[N], bz[N], sum_son[N], fa[N];
int d[N], bz_d[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;
}
void dg(int x) {
if(x == t) return;
bz[x] = 1;
for(int i = final[x]; i; i = next[i]) {
int y = to[i]; if(bz[y]) continue;
sum_son[x] ++; fa[y] = x; dg(y);
}
bz[x] = 0;
}
void Build(int x) {
while(x != s) d[++ d[0]] = x, bz_d[x] = 1, x = fa[x]; d[++ d[0]] = x;
fo(i, 2, d[0]) sum += sum_son[d[i]]; sum -= (d[0] - 1);
}
void dg2(int x) {
if(x == t) return;
bz[x] = 1;
int mx = 0, mx2 = 0;
for(int i = final[x]; i; i = next[i]) {
int y = to[i]; if(bz[y]) continue;
if(!bz_d[y]) w[y] = w[x] + sum_son[y];
dg2(y);
if(w[y] > mx) {
mx2 = mx;
mx = w[y];
} else
if(w[y] > mx2) {
mx2 = w[y];
}
}
w[x] = mx2 + sum_son[x];
bz[x] = 0;
}
int pd(int m) {
int yige = 0;
fd(i, d[0], 2) {
int dep = d[0] - i + 1;
int ss = 0;
int x = d[i];
for(int j = final[x]; j; j = next[j]) {
int y = to[j]; if(bz_d[y] || fa[y] != x) continue;
if(w[y] + sum > m - yige) ss ++;
}
if(ss > dep - yige) return 0;
yige += ss;
if(m < yige) return 0;
sum -= sum_son[x] - 1;
}
return 1;
}
int main() {
freopen("compete.in", "r", stdin);
freopen("compete.out", "w", stdout);
scanf("%d %d %d", &n, &t, &s);
fo(i, 1, n - 1) {
scanf("%d %d", &x, &y);
link(x, y);
}
dg(s);
Build(t);
dg2(s);
int ans = 0;
for(int l = 0, r = n; l <= r; ) {
int m = (l + r) / 2;
int sum2 = sum;
if(pd(m)) ans = m, r = m - 1; else l = m + 1;
sum = sum2;
}
printf("%d", ans);
}