我们在树上的每个点 i i i上放 a i a_i ai个小点,初始时先让每个点单独减,这样要花费 a i a_i ai之和的次数。
然后尝试把某些减合并。一个点上面的小点至多可以向两个相邻的小点连边(这两个小点不能在同一个点上)。每连一条边,合并次数+1,答案-1。
问题变成求一棵树内的最大合并次数。
首先明确该问题满足最优子结构,即考虑以
U
U
U为根的子树时,若
U
,
V
U,V
U,V上的小点
u
,
v
u,v
u,v可以合并,合并
u
,
v
u,v
u,v 一定不比 不合并
u
,
v
u,v
u,v以让
f
a
u
,
u
fa_u,u
fau,u合并 劣。
感性证明:
f
a
u
fa_u
fau可能可以和不是
u
u
u的其它小点合并,即使找不到其它可以合并的小点,前面的方案也不必后面的方案劣。
设
g
i
g_i
gi表示考虑完以
i
i
i为根的子树,在合并次数最多的情况下,
i
i
i上最多有几个小点能和
i
i
i的父亲上的小点合并。转移讨论一下即可。
#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=2e5+10;
struct Edge{int v,nxt;}e[N<<1];
int cnt,head[N],n;
ll ans,a[N];
void adde(int u,int v){
e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;
}
void dfs(int u,int fa){
ll sum=0,maxn=0,pr;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==fa)continue;
dfs(v,u);
sum+=a[v],maxn=max(maxn,a[v]);
}
if(sum-maxn>=maxn) pr=sum/2;
else pr=sum-maxn;
if(sum<=a[u]) ans-=sum;
else{
pr=min(pr,min(sum-a[u],a[u]));
ans-=a[u]+pr;
a[u]-=pr;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);ans+=a[i];
}
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
adde(u,v);adde(v,u);
}
dfs(1,0);
printf("%lld\n",ans);
return 0;
}