思路
题目要求在树上求最短路,我们很容易想到LCA来解决。
但问题我们需要同时计算能获得的最大值,所以我们需要额外的倍增数组来维护一些值。
那么具体是什么呢?
- 假设我们从 x x x点出发,到达 L C A ( x , y ) LCA(x,y) LCA(x,y)的时候,进行了买入操作,在 L C A ( x , y ) LCA(x,y) LCA(x,y)到 y y y的路上,进行了卖出操作,那么我们就需要直到 x − L C A ( x , y ) x-LCA(x,y) x−LCA(x,y)上的最大值, L C A ( x , y ) − y LCA(x,y)-y LCA(x,y)−y上的最小值。所以我们需要两个倍增数组,一个 m a x F maxF maxF维护最大值,一个位置最小值 m i n F minF minF(比如 m a x F [ x ] [ i ] maxF[x][i] maxF[x][i]表示从 x x x到它的 i i i级祖先上最大值是多少)
- 假如我们直接在
x
−
L
C
A
(
x
,
y
)
x-LCA(x,y)
x−LCA(x,y)进行了买入卖出操作,那么我们需要直接计算这个最大值,设
u
p
[
x
]
[
i
]
up[x][i]
up[x][i]表示从
x
x
x到它的
i
i
i祖先进行了先买后卖操作所能获得最大值。那么我们怎么维护更新它呢?
设 y = f [ x ] [ i − 1 ] y = f[x][i-1] y=f[x][i−1],那么最大值可能由以下三种情况中产生:- 在 x − y x-y x−y中先卖后买,即 u p [ x ] [ i − 1 ] up[x][i-1] up[x][i−1]
- 在 y − f [ x ] [ i ] y - f[x][i] y−f[x][i]中先买后卖,即 u p [ y ] [ i − 1 ] up[y][i-1] up[y][i−1]
- 在 x − y x-y x−y中买入,在 y − f [ y ] [ i − 1 ] y - f[y][i-1] y−f[y][i−1]中卖出,即 m a x F [ x ] [ i − 1 ] − m i n F [ x ] [ i − 1 ] maxF[x][i-1] - minF[x][i-1] maxF[x][i−1]−minF[x][i−1]
- 在 L C A ( x , y ) − y LCA(x,y) - y LCA(x,y)−y中进行先买后卖操作,我们新开一个 d o w n [ x ] [ i ] down[x][i] down[x][i]数组,含义与 u p up up类似,但注意,顺序不一样,这个表示从 x x x的 i i i级祖先到它本身,进行先买后卖操作。
一些细节
在求答案的过程中,使用了这种方式遍历倍增数组。
比如
9
=
2
3
+
2
0
9 = 2^3 + 2^0
9=23+20,那么就会先执行
x
′
=
f
[
x
]
[
3
]
x' = f[x][3]
x′=f[x][3],后执行
x
′
′
=
f
[
x
′
]
[
0
]
x'' = f[x'][0]
x′′=f[x′][0]
for(int i = 20; i >= 0; --i)
if(dep & (1 << i)) {
ans = max(ans, max(up[x][i], maxF[x][i] - minNum));
minNum = min(minNum, minF[x][i]);
x = f[x][i];
}
代码
#include <cstdio>
#include <iostream>
using namespace std;
const int maxN = 3e5 + 7;
struct Edge {
int from, to;
}e[maxN << 1];
int n, m, head[maxN], cnt, val[maxN], f[maxN][31], d[maxN], minF[maxN][31], maxF[maxN][31], up[maxN][31], down[maxN][31];
inline void add(int u, int v)
{
e[++cnt].from = head[u];
e[cnt].to = v;
head[u] = cnt;
}
void dfs(int x, int fa)
{
d[x] = d[fa] + 1; f[x][0] = fa;
maxF[x][0] = max(val[x], val[fa]); minF[x][0] = min(val[x], val[fa]);
up[x][0] = max(0, val[fa] - val[x]); down[x][0] = max(0, val[x] - val[fa]);
for(int i = 1; i <= 20; ++i) {
f[x][i] = f[f[x][i - 1]][i - 1];
if(!f[x][i])
break;
int y = f[x][i - 1];
maxF[x][i] = max(maxF[x][i - 1], maxF[y][i - 1]);
minF[x][i] = min(minF[x][i - 1], minF[y][i - 1]);
up[x][i] = max(max(up[x][i - 1], up[y][i - 1]), maxF[y][i - 1] - minF[x][i - 1]);
down[x][i] = max(max(down[x][i - 1], down[y][i - 1]), maxF[x][i - 1] - minF[y][i -1]);
}
for(int i = head[x]; i; i = e[i].from) {
int y = e[i].to;
if(y == fa)
continue;
dfs(y, x);
}
}
inline int Lca(int x, int y)
{
if(d[x] > d[y])
swap(x, y);
for(int i = 21; i >= 0; --i)
if(d[f[y][i]] >= d[x])
y = f[y][i];
if(x == y)
return x;
for(int i = 21; i >= 0; --i)
if(f[x][i] != f[y][i])
x = f[x][i], y = f[y][i];
return f[x][0];
}
inline int query(int x, int y)
{
int lca = Lca(x, y), ans = 0, maxNum = 0, minNum = 0x3f3f3f3f;
int dep = d[x] - d[lca];
if(dep) { // 如果x不是lca(x,y)
for(int i = 20; i >= 0; --i)
if(dep & (1 << i)) {
ans = max(ans, max(up[x][i], maxF[x][i] - minNum));
minNum = min(minNum, minF[x][i]);
x = f[x][i];
}
}
dep = d[y] - d[lca];
if(dep) {
for(int i = 20; i >= 0; --i)
if(dep & (1 << i)) {
ans = max(ans, max(down[y][i], maxNum - minF[y][i]));
maxNum = max(maxNum, maxF[y][i]);
y = f[y][i];
}
}
return max(ans, maxNum - minNum);
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &val[i]);
for(int i = 1; i < n; ++i) {
int x, y; scanf("%d%d", &x, &y);
add(x, y); add(y, x);
}
dfs(1, 0);
scanf("%d", &m);
for(int i = 1; i <= m; ++i) {
int x, y; scanf("%d%d", &x, &y);
printf("%d\n", query(x, y));
}
return 0;
}