给一颗树,每个节点有 \(1\) 或 \(-1\) 两种权值,问对于每一个点,包含它的联通块权值最大是多少。
第一次写换根DP,其实并不难。相反 E 题记忆化搜索没想出来是真的煞笔。
就是一个最板的换根DP,先把 \(1\) 视作根节点,算出每个点的最大子树。
然后再进行树形DP,每次向儿子转移时将自己、父亲、其他儿子的最大总权传给儿子。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <queue>
using namespace std;
typedef long long LL;
const int N=2e5+10;
const int M=1e6+10;
int n,a[N],maxs[N],ans[N];
vector<int> g[N];
void dfs1(int u,int fa){
maxs[u]=a[u];
for(int v:g[u]){
if(v==fa) continue;
dfs1(v,u);
if(maxs[v]>0) maxs[u]+=maxs[v];
}
}
void dfs2(int u,int fa,int w){
if(w<0) w=0;
ans[u]=maxs[u]+w;
for(int v:g[u]){
if(v==fa) continue;
if(maxs[v]>0) dfs2(v,u,w+maxs[u]-maxs[v]);
else dfs2(v,u,w+maxs[u]);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]==0) a[i]=-1;
}
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1,0);
dfs2(1,0,0);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}
CodeForces 1324F. Maximum White Subtree【换根DP】
原文:https://www.cnblogs.com/BakaCirno/p/12484415.html