题解 | CF1689C Infected Tree

树形 DP。

\(size_v\) 为以 \(v\) 为根的子树大小。

\(dp_x\)\(x\) 被感染时其子树能留下几个点。

对于一个被感染的点 \(x\) 的孩子的点 \(v\)(未被感染),作如下考虑:

  • 删除 \(v\),则它的子树全部都得以保存。\(dp_x\) 不能加上 \(dp_v\),但可以加上 \(size_v-1\)\(v\) 本身不能算)。

  • 不删除它,\(dp_x\) 直接加上 \(dp_v\)

这样,对于每个 \(x\),枚举删除哪个孩子即可。

又因为原图是一颗二叉树,可以记一个 \(sum\)\(x\) 所有孩子的 \(dp\) 值之和,以快速求出另一个子树的 \(dp\) 值。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int t, n, sz[300005], dp[300005];
vector<int> vc[300005];
int dfs(int x, int fa){
	sz[x] = 1;
	dp[x] = 0;
	int sum = 0;
	for(int i = 0; i < vc[x].size(); i++){
		int v = vc[x][i];
		if(v == fa) continue;
		sz[x] += dfs(v, x);
		sum += dp[v];
	}
	for(int i = 0; i < vc[x].size(); i++){
		int v = vc[x][i];
		if(v == fa) continue;
		dp[x] = max(sum - dp[v] + sz[v] - 1, dp[x]);
	}
	return sz[x];
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> t;
	while(t--){
		cin >> n;
		for(int i = 1; i <= n; i++) vc[i].clear();
		for(int i = 1; i <= n - 1; i++){
			int a, b;
			cin >> a >> b;
			vc[a].push_back(b);
			vc[b].push_back(a);
		}
		dfs(1, 0);
		cout << dp[1] << '\n';
	}
	return 0;
}
posted @ 2025-07-21 12:54  KukCair  阅读(7)  评论(0)    收藏  举报