「JSOI2013」哈利波特和死亡圣器

「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;
}
原文地址:https://www.cnblogs.com/zsbzsb/p/12283750.html