题意
给一颗树,每个节点有 \(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; }
来源:https://www.cnblogs.com/BakaCirno/p/12484415.html