题解: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;
}
本文来自博客园,作者:KukCair,转载请注明原文链接:https://www.cnblogs.com/KukCair/p/18995587

浙公网安备 33010602011771号