裸的树形dp 比赛时候脑抽up和down和一起写了,没考虑到他们相互更新了,然后就跪了qwq
以任意形态build一棵树,定义数组down[x]记录以x为根节点的x的子树中点的贡献,定义up[x]记录以x为根节点,在初始树种不在x的子树中的点的贡献,则改点作为root的这棵树的权值即为up[x]+down[x],在定义size[x]为以x为根节点的子树的点权之和,那么由于down[x]=down[son[x]]+size[x],可以O(n)维护处down[x],对于up[x],只能从up[fa[x]]和down[bother[x]]共同转移来,所以也可以O(n)维护
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <cstring> using namespace std; const int maxn=400010; int n,st[maxn],nt[maxn],to[maxn],topt,xx,yy; bool f[maxn>>1]; long long ans,v[maxn>>1],siz[maxn>>1],up[maxn>>1],down[maxn>>1],sum; inline void add(int x,int y) {to[++topt]=y; nt[topt]=st[x]; st[x]=topt;} inline void downdp(int x) { f[x]=1; siz[x]=v[x]; int p=st[x]; while (p) { if (!f[to[p]]) { downdp(to[p]); siz[x]+=siz[to[p]]; down[x]+=down[to[p]]+siz[to[p]]; } p=nt[p]; } } inline void updp(int x,long long vv,int fa) { //printf("%d %lld\n",x,vv); up[x]=vv+(sum-siz[x]); f[x]=1; int p=st[x]; while (p) { if (!f[to[p]]) { long long now=up[x]; /* int p1=st[x]; while (p1) { if (p1!=p && to[p1]!=fa) now+=down[to[p1]]+siz[to[p1]]; p1=nt[p1]; }*/ now+=down[x]-(down[to[p]]+siz[to[p]]); updp(to[p],now,x); } p=nt[p]; } } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%lld",&v[i]),sum+=v[i]; for (int i=1;i<=n-1;i++) {scanf("%d%d",&xx,&yy); add(xx,yy); add(yy,xx);} downdp(1); memset(f,0,sizeof f); updp(1,0,0); for (int i=1;i<=n;i++) ans=max(ans,up[i]+down[i]); //for (int i=1;i<=n;i++) printf("%d %lld %lld\n",i,down[i],up[i]); printf("%lld\n",ans); return 0; }