Acesrc和旅行
问题描述
Acesrc是南京大学着名的旅游者之首。在这个暑假期间,他和张和刘一起将前往香港。有ñ 香港的景点,和 n - 1连接这些景点的双向观光巴士路线。他们决定乘公共汽车去一些景点。
然而,张和刘对这些景点有不同的偏好。他们分别为每个地点设定了令人满意的价值。如果他们访问了一世当场,张将获得满意的价值 一个一世,刘将获得 b一世。从张开始,为了公平起见,他们轮流决定下一个访问的地方。当前景点和下一个景点之间必须有一条公交线路。而且,他们永远不会两次去一个地方。如果有人找不到这样的下一个景点,他们别无选择,只能结束这次旅行。
张和刘都是超级聪明的竞争程序员。要么想要最大化他的总满意值与另一个满意度之间的差异。现在Acesrc想知道,如果他们都选择最佳,那么张和刘总的满意度之间有什么区别?
输入
第一行输入由一个整数组成 Ť (1 ≤ Ť≤ 30 ),表示测试用例的数量。
对于每个测试用例,第一行包含一个整数ñ (1 ≤ Ñ ≤ 10五),表示斑点的数量。接下来的两行中的每一行都包含ñ 整数, 一个1,a2,⋯ ,añ 和 b1,b2,⋯ ,bñ (0 ≤ 一一世,b一世≤ 109),分别表示
张和刘对每个地方的满意价值。最后一个n - 1 行包含两个整数 x ,y (1 ≤ X ,ÿ≤ Ñ ,X ≠ ÿ),表示之间的公共汽车路线 X现场和 ÿ现场。可通过这些巴士路线从任何地点到任何其他地点。
它保证了总和ñ 不超过 5.01 × 10五。
产量
对于每个测试用例,在一行中打印一个整数,如果它们都选择最佳,则总满意值之差。
思路:对于每一个点的权值我们令其为ai - bi, 对于A来说,A想要这个值尽可能大。而对于B来说,B想要这个值尽可能小。
首先当根是固定的情况,我们令f[i][0] 表示第i个节点先手走的最大值,f[i][1]表示第i个节点后手走的最小值。
那么我们可以很容易的得到转移方程 f[u][0] = max(f[v][1]) + val[u], f[u][1] = min(f[v][0] ) + val[u] 。所以我们可以通过一遍DFS得到答案。
然而根并不确定,所以接下来我们考虑换根。在换根时之所以要记录一个次大和次小值g , 是因为对某个节点u来说,它的状态可能是从父亲转移过来的,也可能是从儿子转移过来的,在我们换根给儿子v的过程中,儿子v转移得到的可能是 之前它转移给父亲的,所以此时我们就不能再转移最值了,而是次大值(次小值)。
另外由于先手可以选择任意的点开始,所以要去max(f[k][1] ) ( 1 <= k <= n) ,这样对先手来说最优。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls rt << 1
#define rs rt << 1|1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1|1
const int maxn = 1e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f3f;
ll val[maxn], f[maxn][2], g[maxn][2]; //每个结点的权值,最值和次值 0表示先手 1表示后手
vector<int> e[maxn];
void init(int n)
{
for(int i = 0; i <= n; ++i)
{
e[i].clear();
f[i][0] = g[i][0] = -inf;
f[i][1] = g[i][1] = inf;
}
}
void dfs(int u, int fa)
{
for(auto v : e[u])
{
if(v == fa) continue;
dfs(v, u);
if(f[v][1] + val[u] > f[u][0]) //先手
g[u][0] = f[u][0], f[u][0] = f[v][1] + val[u];
else if(f[v][1] + val[u] > g[u][0])
g[u][0] = f[v][1] + val[u];
if(f[v][0] + val[u] < f[u][1]) //后手
g[u][1] = f[u][1], f[u][1] = f[v][0] + val[u];
else if(f[v][0] + val[u] < g[u][1])
g[u][1] = f[v][0] + val[u];
}
if(e[u].size() == 1 && e[u][0] == fa) //叶子结点
f[u][0] = f[u][1] = val[u];
}
void slove(int u, int fa)
{
for(auto v : e[u])
{
if(v == fa) continue;
ll Max = -inf, Min = inf;
if(g[u][0] == -inf) Max = val[u]; //单链的情况
else
{
Max = f[u][0];
if(f[v][1] + val[u] == f[u][0]) //如果父亲的最优值是从儿子转移过来的那么只能用次大的
Max = g[u][0];
}
if(g[u][1] == inf) Min = val[u];
else
{
Min = f[u][1];
if(f[v][0] + val[u] == f[u][1])
Min = g[u][1];
}
if(e[v].size() == 1) //叶子结点
{
f[v][0] = Min + val[v];
f[v][1] = Max + val[v];
continue;
} //对儿子进行转移
if(Max + val[v] < f[v][1])
g[v][1] = f[v][1], f[v][1] = Max + val[v];
else if(Max + val[v] < g[v][1])
g[v][1] = Max + val[v];
if(Min + val[v] > f[v][0])
g[v][0] = f[v][0], f[v][0] = Min + val[v];
else if(Min + val[v] > g[v][0])
g[v][0] = Min + val[v];
slove(v, u);
}
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
int n;
scanf("%d", &n);
init(n);
for(int i = 1; i <= n; ++i)
scanf("%lld", &val[i]);
ll x;
for(int i = 1; i <= n; ++i)
{
scanf("%lld", &x);
val[i] -= x;
}
for(int i = 1; i < n; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, -1);
slove(1, -1);
ll ans = -inf;
for(int i = 1; i <= n; ++i)
ans = max(ans, f[i][1]);
printf("%lld\n", ans);
}
return 0;
}