「JSOI2013」哈利波特和死亡圣器
首先二分,这没什么好说的。
然后就成了一个恒成立问题,就是说我们需要满足最坏情况下的需求。
那么显然在最坏情况下伏地魔是不会走回头路的 因为这显然是白给
那么我们肯定需要在所有它可能去的下一个点都设置防御。
也就是说要对当前ta所在点的所有叶子设防。
那么我们就可以考虑 ( ext{DP}) ,设 (dp_i) 表示在以 (i) 为根的子树中设防(注意这里不包括 (i) )还需要多少成员。
那么转移就是:(dp_u = max{sumlimits_{fa_v = u} dp_v + son_u - mid, 0})
其中 (son_u) 表示 (u) 的儿子个数,方程的意思就是说我们需要在 (i) 的所有孩子中设防并且要在孩子的子树里设防,显然我们不会需要负数的人(其实就是表示人多了)所以减掉 (mid) 后对 (0) 取个 (max) 。
#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
template < class T > inline T max(T a, T b) { return a > b ? a : b; }
template < class T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
const int _ = 3e5 + 5;
int tot, head[_]; struct Edge { int v, nxt; } edge[_ << 1];
inline void Add_edge(int u, int v) { edge[++tot] = (Edge) { v, head[u] }, head[u] = tot; }
int n, son[_], dp[_];
inline void dfs(int u, int f) {
for (rg int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].v; if (v == f) continue ;
dfs(v, u), ++son[u];
}
}
inline void dfs(int u, int f, int mid) {
dp[u] = son[u] - mid;
for (rg int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].v; if (v == f) continue ;
dfs(v, u, mid), dp[u] += dp[v];
}
dp[u] = max(dp[u], 0);
}
inline bool check(int mid) { dfs(1, 0, mid); return dp[1] == 0; }
int main() {
#ifndef ONLINE_JUDGE
file("cpp");
#endif
read(n);
for (rg int u, v, i = 1; i < n; ++i) read(u), read(v), Add_edge(u, v), Add_edge(v, u);
dfs(1, 0);
int l = 0, r = n - 1;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid; else l = mid + 1;
}
printf("%d
", l);
return 0;
}