对于这个题目,我的做法是换根dp, 因为可以选择将某一个点染成白色(不论之前的点是黑是白) , 做法如下:
将每个点当成根节点, 然后将根节点染成白色, 完全符合题意;;
首先dfs一下, 自底向上,求出每一个点在子树内的白色连通块大小。
然后从上到下开始算对孩子节点的贡献::
如果当前点是黑色, 那么对儿子节点肯定没有贡献 , 因为上面的所有节点都被这个黑色节点隔开了,下面的节点不能和上面的节点连接;
如果当前节点是白色 :
如果孩子节点是黑色, 那么在第一次dfs的时候,孩子节点肯定对这个父亲节点没有贡献, 那么就当前节点对孩子节点直接传递下去dp[u]
如果孩子节点是白色, 那么在第一次dfs的时候,孩子节点肯定对这个父亲节点有贡献, 那么当前节点对孩子节点的贡献就是dp[u] - dp[x] ,剪掉孩子的贡献, 避免重复计算
#include <iostream> #include <cstdio> #include <algorithm> #include <unordered_map> #include <vector> #include <map> #include <list> #include <queue> #include <cstring> #include <cstdlib> #include <ctime> #include <cmath> #include <stack> #pragma GCC optimize(3 , "Ofast" , "inline") using namespace std ; typedef long long ll ; const double esp = 1e-6 , pi = acos(-1) ; typedef pair<int , int> PII ; const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7; int in() { int x = 0 , f = 1 ; char ch = getchar() ; while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;} while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ; return x * f ; } int col[N] , dp[N] , n ; vector<int> v[N] ; void dfs(int u , int f) { dp[u] = col[u] ; for(auto x : v[u]) { if(x == f) continue ; dfs(x , u) ; if(col[x]) dp[u] += dp[x] ; } return ; } int ans = 0 ; void dfs1(int u , int f , int sum) { // cout << u << " " << f << " " << sum << " " << dp[u] << endl ; dp[u] = dp[u] + sum + (col[u] == 0); ans = max(ans , dp[u]) ; for(auto x : v[u]) { if(x == f) continue ; dfs1(x , u , (col[u] ?(col[x] ? dp[u] - dp[x] : dp[u]):0)) ; } } int main() { n = in() ; string s ; cin >> s ; s = " " + s ; for(int i = 1 ; i <= n ;i ++ ) col[i] = s[i] == 'W' ; for(int i = 1; i < n ;i ++ ) { int a = in() , b = in() ; v[a].push_back(b) , v[b].push_back(a) ; } dfs(1 , 0) ; dfs1(1 ,0 , 0) ; cout << ans << endl ; return 0 ; } /* 13 WBWWBBBBBWWWB 1 2 1 3 2 4 2 5 3 6 3 7 5 8 8 10 8 11 7 9 9 12 9 13 4 */