题意#
给定一棵 个节点无根树,每个节点 有一个颜色 ,若 为 则 是黑点,若 为 则 是白点。
对于每个节点 ,选出一个包含 的连通子图,设子图中白点个数为 ,黑点个数为 ,请最大化 。并输出这个值。
,。
样例#
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
plaintext2 2 2 2 2 1 1 0 2
plaintext分析#
给每个点一个权值 ,白点 ,黑点 ,问题转化为求包含 的最大连通子图。
考虑换根 DP。
先随便定一个点作为根,不妨设为 。在这棵有根树中定义状态。
设 表示:在 的子树中且包含节点 的 最大连通块大小,转移显然:
只要子树答案 ,就可以直接加上来。 表示节点 在以 为根时的父亲。
接下来考虑换根的过程。
对于节点 ,如果以 为根,那么还剩下 连向 的这棵子树 没有统计进答案。
容斥。可以通过以 为根,用整棵树减去 所在的子树得到这棵子树。
设 表示节点 最终的答案。在处理节点 时,我们希望已经计算出了 ,其代表整棵树的信息。
用其减去第一遍 DP 时 对 的贡献,就可以得到 的子树对 的贡献了。
具体地,在有根树上,按照从根到叶子的顺序转移:
将父亲的答案消去自身影响后当成自己的子树,计算其对答案的贡献。
代码实现上,不必建立两个数组。注意第一次 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