枚举一个必选的点,把它作为根,那么其它点依赖且只依赖于它的父亲。用最大权闭合子图求解。
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=100000,oo=0x3f3f3f3f;
int fir[maxn],ne[maxn],to[maxn],w[maxn],val[maxn],dep[maxn],que[maxn],n,s,t,num;
vector<int> tree[2][maxn];
void check()
{
for (int i=0;i<=t;i++)
for (int j=fir[i];j;j=ne[j])
if (w[j]) printf("%d->%d:%d\n",i,to[j],w[j]);
}
void add(int u,int v,int x)
{
num++;
ne[num*2]=fir[u];
fir[u]=num*2;
to[num*2]=v;
w[num*2]=x;
ne[num*2+1]=fir[v];
fir[v]=num*2+1;
to[num*2+1]=u;
w[num*2+1]=0;
}
void dfs0(int f,int u,int fa)
{
for (int v:tree[f][u])
if (v!=fa)
{
add(v,u,oo);
dfs0(f,v,u);
}
}
int bfs()
{
int hd=1,tl=1,u,v;
for (int i=0;i<=t;i++) dep[i]=0;
dep[que[1]=s]=1;
while (hd<=tl)
{
u=que[hd++];
for (int i=fir[u];i;i=ne[i])
if (w[i]&&!dep[v=to[i]])
{
dep[v]=dep[u]+1;
que[++tl]=v;
}
}
return dep[t];
}
int dfs(int u,int lim)
{
int x,ret=0,v;
if (u==t) return lim;
for (int i=fir[u];i&&ret<lim;i=ne[i])
if (w[i]&&dep[v=to[i]]==dep[u]+1)
{
x=dfs(v,min(lim-ret,w[i]));
w[i]-=x;
w[i^1]+=x;
ret+=x;
}
if (ret<lim) dep[u]=0;
return ret;
}
int main()
{
int ans=0,res,x,y;
scanf("%d",&n);
for (int i=0;i<n;i++) scanf("%d",&val[i]);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
tree[0][x].push_back(y);
tree[0][y].push_back(x);
}
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
tree[1][x].push_back(y);
tree[1][y].push_back(x);
}
s=n,t=n+1;
for (int i=0;i<n;i++)
{
for (int j=0;j<=n+1;j++) fir[j]=0;
num=0;
res=val[i];
for (int j=0;j<n;j++)
if (j!=i)
{
if (val[j]>0)
{
res+=val[j];
add(s,j,val[j]);
}
else add(j,t,-val[j]);
}
for (int v:tree[0][i]) dfs0(0,v,i);
for (int v:tree[1][i]) dfs0(1,v,i);
//check();
while (bfs()) res-=dfs(s,oo);
ans=max(ans,res);
}
printf("%d\n",ans);
}