TonyYin's Blog

Back

题意#

给定一棵 nn 个节点无根树,每个节点 uu 有一个颜色 aua_u,若 aua_u00uu 是黑点,若 aua_u11uu 是白点。

对于每个节点 uu,选出一个包含 uu 的连通子图,设子图中白点个数为 cnt1cnt_1,黑点个数为 cnt2cnt_2,请最大化 cnt1cnt2cnt_1 - cnt_2。并输出这个值。

1n2×1051 \leq n \leq 2 \times 10^50au10 \leq a_u \leq 1

样例#

9
0 1 1 1 0 0 0 0 1
1 2
1 3
3 4
3 5
2 6
4 7
6 8
5 9
plaintext
2 2 2 2 2 1 1 0 2
plaintext

分析#

给每个点一个权值 colicol_i,白点 coli=1col_i=1,黑点 coli=1col_i=-1,问题转化为求包含 uu 的最大连通子图。

考虑换根 DP。

先随便定一个点作为根,不妨设为 11。在这棵有根树中定义状态。

gug_u 表示:ii 的子树中且包含节点 ii 最大连通块大小,转移显然:

gu=colu+fatherv=umax(0,gv)g_u = col_u+\sum_{\mathrm{father}_v=u}{\max(0, g_v)}

只要子树答案 >0>0,就可以直接加上来。fatherv\mathrm{father}_v 表示节点 vv 在以 11 为根时的父亲。

接下来考虑换根的过程。

对于节点 uu,如果以 uu 为根,那么还剩下 uu 连向 fatheru\operatorname{father}_u 的这棵子树 没有统计进答案。

容斥。可以通过以 11 为根,用整棵树减去 uu 所在的子树得到这棵子树。

fif_i 表示节点 ii 最终的答案。在处理节点 uu 时,我们希望已经计算出了 ffatheruf_{\operatorname{father}_u},其代表整棵树的信息。

用其减去第一遍 DP 时 uufatheru\operatorname{father}_u 的贡献,就可以得到 fatheru\operatorname{father}_u 的子树对 fuf_u 的贡献了。

具体地,在有根树上,按照从根到叶子的顺序转移:

fu=gu+max(0,ffatherumax(0,gu))f_u=g_u+\max(0, f_{\operatorname{father}_u} - \max(0, g_u))

将父亲的答案消去自身影响后当成自己的子树,计算其对答案的贡献。

代码实现上,不必建立两个数组。注意第一次 DP 顺序是从叶子到根,第二次是从根到叶子。

代码#

void dfs1(int x, int fa) {
	for(int v : e[x]) if(v != fa) {
		dfs1(v, x);
		if(dp[v] > 0)
			dp[x] += dp[v];
	}
}
void dfs2(int x, int fa) {
	if(x != 1)
		dp[x] += max(0, dp[fa] - max(dp[x], 0));
	for(int v : e[x])
		if(v != fa) dfs2(v, x);
}
cpp
【CodeForces-1324F】Maximum White Subtree
https://www.tonyyin.top/blog/oi-solution/cf1324f
Author TonyYin
Published at October 27, 2021
Comment seems to stuck. Try to refresh?✨