题目大意
给一颗树,每个节点的颜色为黑色或白色,求包含节点 的所有连通子图中,白色节点数与黑色节点数的最大差值。
分析过程
设 为包含节点 所能获得的最大贡献,很显然,这个贡献就等于当把 作为全树的根时,其子树的正贡献之和+其自身的贡献,我们发现好像并不能直接计算这个答案。我们可以这样做,先用 预处理一下,求出每个节点的子树对其的贡献(含自身贡献),即 这个时候的 只求出了节点下方子树的贡献;这个时候,我们再用 进行最终答案的计算,从根开始(指定1为根)遍历树,每一次更新子树节点的答案,即 第二遍 的实现本质上就是在轮流换根求贡献。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 100;
int a[maxn], n, dp[maxn];
vector<int> G[maxn];
void dfs(int node, int prt){ //计算以1为整棵树的根,node为当前子树根时,其下方子树对其的贡献+该节点自身贡献
int i;
dp[node] += a[node];
for(i=0;i<G[node].size();++i){
int to = G[node][i];
if(prt == to) continue;
dfs(to, node); //递归计算子树贡献
dp[node] += max(0, dp[to]);
}
}
void dfs2(int node, int prt){ //每次以to作为整棵树的根,即叠加上方对to产生的贡献
int i;
for(i=0;i<G[node].size();++i){
int to = G[node][i];
if(prt == to) continue;
dp[to] += max(0, dp[node] - max(0, dp[to]));
dfs2(to, node);
}
}
int main(){
int t, i, j, u, v;
ios::sync_with_stdio(false);
cin>>n;
for(i=1;i<=n;++i){
cin>>a[i];
if(!a[i]) a[i] = -1;
}
for(i=1;i<n;++i){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);S
}
dfs(1, -1);
dfs2(1, -1);
for(i=1;i<n;++i) cout<<dp[i]<<' ';
cout<<dp[i];
return 0;
}