题意:
有两个人在树上博弈,每个点节点有两个分数a[i]和b[i],先手先选择一个点,后手在先手选的点的相邻点中选择一个点,然后先手在后手选的点的相邻点中选择一个两个人都没有走过的点,直到不能走,游戏就结束。一个人走到节点x,那么先手会获得分数a[x],后手就会会获得分数b[x]。最后询问先手能获得与后手的差值最大值。
思路:
换根的思路,先随便选择一个点作为根,算出以该点作为根的答案。然后再用一个dfs转换到以别的点为根的情况下的答案。
为了方便直接把a和b做差,每个点记录差值来代替a和b。
f
(
u
,
p
,
0
)
=
max
f
(
v
,
u
,
1
)
+
w
u
f(u,p,0) = \max f(v,u,1) + w_{u}
f(u,p,0)=maxf(v,u,1)+wu
f
(
u
,
p
,
1
)
=
min
f
(
v
,
u
,
0
)
+
w
u
f(u,p,1) = \min f(v,u,0) + w_{u}
f(u,p,1)=minf(v,u,0)+wu
以上两个公式是官方题解给出的,下标为0表示的是先手选择的状态,下标为1则是后手选择的状态。
不难理解,先手选择的时候会从所有相邻点后手选择的情况中选择一个最大的,然后再加上当前这个点的贡献。而后手则相反,每次从相邻点的先手选择状态中选择一个最小的,再加上当前这个点的贡献。
于是就用 d p [ x ] [ 2 ] dp[x][2] dp[x][2]来表示状态就可以了。
在换根的时候会发现如果当前的点是u,u有一个子节点v,换根的时候本质就是要把父节点当成当前节点的一个子节点,也就是说v这个点的状态需要继承父节点u的状态,当把v作为根的时候会发现,如果u的最大值或者最小值本身就是来源于v这个点的,v再去继承本来就是从自己继承出去的状态就不合理了,因此需要记录每个点的次大值和次小值,刚才的情况v就可以继承u的次值了,这个次值继承于u的其它儿子或者父亲。如果u的极值不是继承于v,那么v可以直接继承这个极值,因为极值也是继承于u的其它儿子或者父亲。
所以再开一个 d p 2 [ x ] [ 2 ] dp2[x][2] dp2[x][2]用来存放每个点的次大值或者次小值。
还有一些细节需要考虑,如就两个点的时候,记u-v,u本身就只有一个极值且继承于v,换根的的时候,v要继承就要特判为u本身的值了。这种情况可以形容为一个点只有一个儿子,树中的叶子节点都是这种情况,所有叶子节点都要特判一下。
参考代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1e18+7;
const int N=1e5+5;
ll a[N];
int head[N],cnt;
struct _edge{
int v,nxt;
}edge[N<<2];
void addedge(int u,int v){
edge[++cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
ll dp[N][2];
ll dp2[N][2];
ll ans=0;
bool isleaf[N];//表示一个点是否为叶子
void dfs(int u,int fa){
bool end=true;
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa)continue;
dfs(v,u);
end=false;
if(dp[v][1]<dp2[u][0])dp2[u][0]=dp[v][1];
if(dp2[u][0]<dp[u][0])swap(dp[u][0],dp2[u][0]);
if(dp[v][0]>dp2[u][1])dp2[u][1]=dp[v][0];
if(dp2[u][1]>dp[u][1])swap(dp[u][1],dp2[u][1]);
}
isleaf[u]=end;
if(end){
dp[u][0]=dp[u][1]=0;
}
if(dp[u][0]!=INF)dp[u][0]+=a[u];
if(dp[u][1]!=-INF)dp[u][1]+=a[u];
if(dp2[u][0]!=INF)dp2[u][0]+=a[u];
if(dp2[u][1]!=-INF)dp2[u][1]+=a[u];
}
void dfs2(int u,int fa){
for(int i=head[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(u!=1&&v==fa)continue;
ll minn=dp[v][1]+a[u]==dp[u][0]?dp2[u][0]:dp[u][0];//父亲极值来源于v的话,v就要继承父亲的次值
ll maxn=dp[v][0]+a[u]==dp[u][1]?dp2[u][1]:dp[u][1];
//叶子节点的父亲就只有一个极值,没有次值,就会出现下面情况
if(maxn==-INF)maxn=a[u];
if(minn==INF)minn=a[u];
maxn+=a[v];
minn+=a[v];
if(isleaf[v]){
ans=max(ans,maxn);
}
if(maxn<dp2[v][0])dp2[v][0]=maxn;
if(dp2[v][0]<dp[v][0])swap(dp[v][0],dp2[v][0]);
if(minn>dp2[v][1])dp2[v][1]=minn;
if(dp2[v][1]>dp[v][1])swap(dp[v][1],dp2[v][1]);
ans=max(ans,dp[v][0]);
dfs2(v,u);
}
}
int main(){
int t;
scanf("%d",&t);
for(int ca=1;ca<=t;ca++){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
dp[i][0] = dp2[i][0] = INF;
dp[i][1] = dp2[i][1] = -INF;
head[i]=-1;
scanf("%lld",&a[i]);
}
ll x;
for(int i=1;i<=n;i++){
scanf("%lld",&x);
a[i]-=x;
}
cnt=0;
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs(1,0);
ans=dp[1][0];
dfs2(1,0);
printf("%lld\n",ans);
}
return 0;
}