CF1324F Maximum White Subtree

CF1324F Maximum White Subtree

Problem

给定一棵包含 \(n\) 个顶点的树。树是一个包含 \(n-1\) 条边的连通无向图。树上的每个顶点 \(v\) 都被赋予了一种颜色(如果顶点 \(v\) 是白色,则 \(a_v = 1\),如果顶点 \(v\) 是黑色,则 \(a_v = 0\))。

你需要对于每个顶点 \(v\),解决如下问题:在所有包含顶点 \(v\) 的子树中,白色顶点数与黑色顶点数的最大差值是多少?树的子树是原树的一个连通子图。更正式地说,如果你选择的子树包含 \(cnt_w\) 个白色顶点和 \(cnt_b\) 个黑色顶点,你需要最大化 \(cnt_w - cnt_b\)

Solution

非常简单但很精妙的换根

观察到对于每个点都要求,但应该用不到点分树之类的玩意儿,那只能是换根了

一个显然的trick是定义\(col_u\),如果是白色则为1,黑色为-1

一个根(通常取 \(1\)),设 \(f_u\) 表示在以 \(u\) 为根的子树内,必须包含 \(u\) 的连通块的最大点权和。转移时,对于每个子节点 \(v\),若 \(f_v > 0\) 则将其贡献加入,否则不选该子树。于是有:

\[f_u = col_u + \sum_{v \in son(u)} \max(0, f_v) \]

\(g_u\) 表示以 \(u\) 为整棵树的根时,必须包含 \(u\) 的连通块的最大点权和

首先 \(g_1 = f_1\)

对于 \(u\) 的子节点 \(v\),考虑将根从 \(u\) 换到 \(v\) 时,\(v\) 的答案由两部分组成:

  • \(v\) 为根的原子树部分:即 \(f_v\)
  • \(v\) 为根时,父节点 \(u\) 方向的新子树部分:即 \(g_u\) 去掉 \(v\) 这棵子树的贡献后的值。

具体地,\(g_u\) 中来自 \(v\) 子树的贡献为 \(\max(0, f_v)\)。因此,去掉该贡献后 \(u\) 方向的部分值为:

\[remain = g_u - \max(0, f_v) \]

当根换到 \(v\) 时,\(v\) 可以选择是否连接 \(u\) 方向的这一部分:若 \(remain > 0\),则将其加入;否则不连。因此:

\[g_v = f_v + \max(0, remain) = f_v + \max(0, g_u - \max(0, f_v)) \]

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, col[N], f[N], g[N];
vector <int> e[N];
void dfs(int u, int fa) {
    f[u] = col[u];
    for (auto v : e[u]) 
        if (v != fa) dfs(v, u), f[u] += max(f[v], 0);
}
void dfs2(int u, int fa) {
    if (fa != -1) g[u] = max(0, g[fa] - max(0, f[u])) + f[u]; else g[u] = f[u];
    for (auto v : e[u])
        if (v != fa) dfs2(v, u);
}
int main() {
    cin.tie(nullptr) -> ios::sync_with_stdio(0);
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> col[i], col[i] = col[i] ? 1 : -1;
    for (int u, v, i = 1; i < n; i++) cin >> u >> v, e[u].emplace_back(v), e[v].emplace_back(u);
    dfs(1, -1); dfs2(1, -1);
    for (int i = 1; i <= n; i++) cout << g[i] << ' ';
    return 0;
}
posted @ 2026-04-19 07:44  Aojun  阅读(16)  评论(0)    收藏  举报