题解:P4572 [JSOI2013] 哈利波特与死亡圣器

二分

我们发现答案具有单调性。

考虑二分的 check 怎么写。

check

首先大军肯定会往下走,往回走是不划算的,纯浪费时间;

而凤凰社会启用大军正在攻克节点的子节点。

设派出 \(x\) 人。

我们设 \(dp_i\) 为以 \(i\) 为根的子树需要从其他地方调来的人,则有

\[dp_i\gets\max\{(\sum_{v=i \text{的子节点}} (dp_v+1)) - x, 0\} \]

  • 为什么要和 \(0\)\(\max\)

    这个时候若 \(dp_i<0\),按照我们的定义是指调走人帮助兄弟节点,但由于我们并不知道应帮助谁,所以只能取 \(0\)

  • 为什么要 \(dp_v+1\)

    需要开启 \(v\) 本身。

Code

#include <bits/stdc++.h>
using namespace std;
int n, dp[300005];
vector<int> vc[300005];
void dfs(int x, int fa, int k){
	for(int i = 0; i < vc[x].size(); i++){
		int v = vc[x][i];
		if(v != fa){
			dfs(v, x, k);
			dp[x] += dp[v] + 1;
		}
	}
	dp[x] = max(0, dp[x] - k);
}
bool check(int x){
	memset(dp, 0, sizeof(dp));
	dfs(1, 0, x);
	if(dp[1] > 0) return 0;
	return 1;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n;
	for(int i = 1; i <= n - 1; i++){
		int u, v;
		cin >> u >> v;
		vc[u].push_back(v);
		vc[v].push_back(u);
	}
	int l = 0, r = n - 1;
	while(l < r){
		int mid = (l + r) / 2;
		if(check(mid)) r = mid;
		else l = mid + 1;
	}
	cout << l;
	return 0;
}
posted @ 2025-07-21 12:57  KukCair  阅读(7)  评论(0)    收藏  举报