点击这里查看原题
首先可以想到n^2做法,枚举每一条边,切断这条边变成两棵树,对两棵树各O(n)求一遍重心,加和即为答案
但是题目中有一个重要条件,高度h不超过100,因此可以O(h)求重心,即每次向权值和最大的儿子转移(维护时需要维护最大和次大)。
总复杂度O(nh)
/*
User:Small
Language:C++
Problem No.:3302
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=50005;
const ll inf=(ll)1<<60;
int n,tot=1,fir[M],w[M],cut,mx[M][2],dep[M],fa[M];
ll g[M],ans=inf,sum[M],sumall,a,b;
struct edge{
int v,nex;
}e[M<<1];
void add(int u,int v){
e[++tot]=(edge){v,fir[u]};
fir[u]=tot;
}
void dfs(int u,int f){
fa[u]=f;
for(int i=fir[u];i;i=e[i].nex){
int v=e[i].v;
if(v==f) continue;
dep[v]=dep[u]+1;
dfs(v,u);
sum[u]+=sum[v];
g[u]+=g[v]+sum[v];
if(sum[v]>sum[mx[u][0]]) mx[u][1]=mx[u][0],mx[u][0]=v;
else if(sum[v]>sum[mx[u][1]]) mx[u][1]=v;
}
}
void getcen(int tp,int u,ll k,ll &res){
res=min(res,k);
int v=mx[u][0];
if(v==cut||sum[mx[u][1]]>sum[mx[u][0]]) v=mx[u][1];
if(!v) return;
getcen(tp,v,k+sum[tp]-2*sum[v],res);
}
void solve(int u){
for(int i=fir[u];i;i=e[i].nex){
int v=e[i].v;
if(v==fa[u]) continue;
cut=v;
for(int k=u;k;k=fa[k]) sum[k]-=sum[v];
a=b=inf;
getcen(1,1,g[1]-g[v]-dep[v]*sum[v],a);
getcen(v,v,g[v],b);
ans=min(ans,a+b);
for(int k=u;k;k=fa[k]) sum[k]+=sum[v];
solve(v);
}
}
int main(){
freopen("data.in","r",stdin);//
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++) scanf("%lld",&sum[i]);
dfs(1,0);
solve(1);
printf("%lld\n",ans);
return 0;
}