CF1324 --- Maximum White Subtree
阅读原文时间:2023年07月08日阅读:1

题干

You are given a tree consisting of \(n\) vertices. A tree is a connected undirected graph with \(n−1\) edges. Each vertex \(v\) of this tree has a color assigned to it (\(a_v\)=1 if the vertex \(v\) is white and \(0\) if the vertex \(v\) is black).

You have to solve the following problem for each vertex \(v\): what is the maximum difference between the number of white and the number of black vertices you >can obtain if you choose some subtree of the given tree that contains the vertex \(v\)? The subtree of the tree is the connected subgraph of the given tree. More >formally, if you choose the subtree that contains \(cnt_w\) white vertices and \(cnt_b\) black vertices, you have to maximize \(cnt_w−cnt_b\).

\(\mathcal{Input}\)

The first line of the input contains one integer \(n\) (\(2 \leq n \leq 2 * 10^5\)) — the number of vertices in the tree.

The second line of the input contains \(n\) integers \(a_1,a_2,\cdots, a_n (0\leq a_i \leq 1)\), where \(a_i\) is the color of the \(i-th\) vertex.

Each of the next \(n−1\) lines describes an edge of the tree. Edge \(i\) is denoted by two integers \(u_i\) and \(v_i\), the labels of vertices it connects \((1 \leq u_i,v_i \leq n,u_i \not= v_i)\).

It is guaranteed that the given edges form a tree.

\(\mathcal{Output}\)

Print \(n\) integers \(res_1, res_2, \cdots, res_n\), where \(res_i\) is the maximum possible difference between the number of white and black vertices in some subtree that contains the vertex \(i\).

\(\mathcal{Example}\)

\(Case_1\)

\(Input\)

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

\(Output\)

2 2 2 2 2 1 1 0 2

\(Case_2\)

\(Input\)

4

0 0 1 0

1 2

1 3

1 4

\(Output\)

0 -1 1 -1

\(\mathcal{Note}\)

The first example is shown below:

The black vertices have bold borders.

In the second example, the best subtree for vertices \(2\),\(3\) and \(4\) are vertices \(2\),\(3\) and \(4\) correspondingly. And the best subtree for the vertex \(1\) is the subtree consisting of vertices \(1\) and \(3\).

\(\mathcal{Tag}\)

dfs and similar dp *1800

思路分析

 这题要求的是求出对任何一个vertex \(v\),求出包含这个节点的子树\(cnt_w - cnt_b\)的最大值。

暴力想法

 首先思考下暴力写法应该如何写。

 所以,对于所有可能的路径的贡献值的累加,且贡献值需大于等于\(0\).不妨设\(dp[v]\)代表该结点的最大值。故

\[dp[v] = a_v + \sum_{i=0}^{k}max(0, dp[i])
\]

 假如用暴力写法,就是对于每个结点\(v\),暴力搜索所有的adjacent结点,利用\(dfs\)暴力搜索。但是结点最大为\(2*10^5\)这个暴力算法显然会超时,考虑如何优化。

算法优化

 对于从下往上的贡献,可以利用从下往上的\(dfs\)树形\(dp\)进行获取,剩余的就是刨去以\(v\)为根的子树的贡献值比较难求。在这里我们设\(fa\)为结点\(v\)的父节点。\(f_v\)代表从下往上以\(v\)为根的白点数减去黑点数的最大值.\(dp[v]\)代表最终的最大值。因此根据刨去以\(v\)为根的子树的贡献值这个思想,我们可以发现:

\[add = dp[fa] - max(0, f_v)
\]

 就是刨去以\(v\)为根的子树的贡献值。因此最终我们可以写出状态转移方程:

\[ dp[v] =\left\{\begin{array}[ll]
1f[v] & if \;v = root \\
f[v] + max(0, dp[fa] - max(0, f[v])) & if\; v \not= root\\
\end{array}\right.
\]

 因此最后我们的思路为:

  • 从下往上树形\(dp\),计算\(f_v\)
  • 从上往下换根\(dp\),计算\(dp[v]\)

代码

#include<bits/stdc++.h>
using namespace std;
using VI = vector<int>;
using VVI = vector<VI>;

VI a;
VI dp;
VI ans;
VVI e;

void dfs(int x, int fa = -1){
    dp[x] = a[x];
    for (int to : e[x]){
        if (to == fa) continue;
        dfs(to, x);
        dp[x] += max(0, dp[to]);
    }
}

void rdfs(int x, int fa = -1){
    ans[x] = dp[x];
    for (int to : e[x]){
        if (to == fa) continue;
        dp[x] -= max(0, dp[to]);
        dp[to] += max(0, dp[x]);
        rdfs(to, x);
        dp[to] -= max(0, dp[x]) ;
        dp[x] += max(0, dp[to]);
    }
}

int main(){
    int n; cin >> n;
    a = dp = ans = VI(n);
    e = VVI(n);

    for (int i = 0; i < n; ++ i){
            cin >> a[i];
            if (a[i] == 0) a[i] = -1;
    }
    for (int i = 0; i < n - 1; ++ i){
        int x, y;
        cin >> x >> y;
        -- x, -- y;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(0);
    rdfs(0);

    for (int ret : ans) cout << ret << " ";
    cout << endl;
    return 0;
}

 第一次在博客园写题解,加油加油! 每天一个CF题!