题解:luogu P3554 [POI 2013] LUK-Triumphal arch
题目链接:https://www.luogu.com.cn/problem/P3554
首先注意到 \(k\) 具有单调性,考虑二分 \(k\)。
接下来考虑 check 的部分,注意到一个关键性质:B 不会走回头路,这样一定不优。
考虑 dp,设 \(f_i\) 表示在节点 \(i\) 为根的子树中,A 现有的 \(k\) 次染色次数不够用,还需要 \(f_i\) 次。当 \(f_i\) 为负表示剩余 \(f_i\) 次染色机会。
看转移:\(f_i=g_i-k+\sum_{j\in son_i} [f_j >0]f_j\)(\(g_i\) 表示 \(i\) 的儿子数量)。
首先假设 B 到 \(i\) 了,那么显然 A 要把 \(i\) 的儿子都染黑,这部分的染色次数为 \(g_i\)。当然,如果儿子不够用要找它借,这部分的代价为 \(\sum [f_j>0]f_j\)。
如果 \(f_1>0\) 那么不可行,否则可行。
解释一下为什么 \(f_j\) 必须为正才累加贡献:B 肯定是先走 \(i\) 再走到 \(j\),那么肯定先要满足 \(i\) 的儿子被染黑,\(j\) 的操作必须在 \(i\) 之后。
时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+5;
vector<int> g[N];
int son[N], dp[N];
inline void dfs(int x, int fa, int k){
dp[x] = son[x] - k;
for(int y : g[x]){
if(y == fa) continue;
dfs(y, x, k);
if(dp[y] > 0) dp[x] += dp[y];
}
}
signed main(){
ios::sync_with_stdio(0);
int n; cin >> n;
for(int i = 1; i < n; i++){
int u, v; cin >> u >> v;
g[u].push_back(v), g[v].push_back(u);
}
int l = g[1].size(), r = g[1].size(), ans = 0;
for(int i = 2; i <= n; i++) son[i] = g[i].size() - 1, r = max(r, son[i]);
son[1] = g[1].size();
while(l <= r){
int mid = (l + r) / 2;
dfs(1, 0, mid);
if(dp[1] <= 0) ans = mid, r = mid - 1;
else l = mid + 1;
}
cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号