题意很简单
给一个树(n < 5w) 每个点有个权值,代表商品价格
若干个询问(5w)
对每个询问,问的是从u点走到v点(简单路径),商人在这个路径中的某点买入商品,然后在某点再卖出商品, 最大可能是多少
注意一条路径上只能买卖一次,先买才能卖
用的方法是离线LCA,在上面加了一些东西
对于一个询问, 假设u,v的LCA是f
那么有三种可能, 一个是从u到f 买卖了。 一个是从f到v买卖了, 一个是从u到f之间买了,从v到f卖了
从u到f 我们称为up, 从f到v我们称为down,而从u到f买然后在f到v卖就是求u到f的最小值和f到v的最大值。
我们要分别求这三个值
实际上就是在离线tarjan求LCA的过程中求出的
对于每个点, 我们进行dfs的时候,查看于其相关的询问,
假设当前点是u, 询问的点是v, u和v的LCA是f,如果v已经dfs过了,说明v在并查集中的祖先就是u,v的LCA f点,
将该询问加入到f的相关集合中,等f所有的子节点都处理过后再去处理f, 就可以发现,一切都是顺其自然了
在这些处理过程中,up和down以及u,v到f的最大值最小值 都可以在并查集求压缩路径的过程中更新。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <map>
#include <ctime>
#define MAXN 52222
#define MAXM 222222
#define INF 1000000001
using namespace std;
vector<int>g[MAXN], st[MAXN], ed[MAXN], id[MAXN], ask[MAXN], pos[MAXN];
int mx[MAXN], mi[MAXN], up[MAXN], down[MAXN], vis[MAXN], fa[MAXN], ans[MAXN];
int n, Q;
int find(int x) {
if(x == fa[x]) return x;
int y = fa[x];
fa[x] = find(y);
up[x] = max(up[x], max(mx[y] - mi[x], up[y]));
down[x] = max(down[x], max(mx[x] - mi[y], down[y]));
mx[x] = max(mx[x], mx[y]);
mi[x] = min(mi[x], mi[y]);
return fa[x];
}
void tarjan(int u) {
vis[u] = 1;
for(int i = 0; i < ask[u].size(); i++) {
int v = ask[u][i];
if(vis[v]) {
int t = find(v);
int z = pos[u][i];
if(z > 0) {
st[t].push_back(u);
ed[t].push_back(v);
id[t].push_back(z);
} else {
st[t].push_back(v);
ed[t].push_back(u);
id[t].push_back(-z);
}
}
}
for(int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if(!vis[v]) {
tarjan(v);
fa[v] = u;
}
}
for(int i = 0; i < st[u].size(); i++) {
int a = st[u][i];
int b = ed[u][i];
int t = id[u][i];
find(a);
find(b);
ans[t] = max(up[a], max(down[b], mx[b] - mi[a]));
}
}
int main() {
scanf("%d", &n);
int u, v, w;
for(int i = 1; i <= n; i++) {
scanf("%d", &w);
mx[i] = mi[i] = w; fa[i] = i;
}
for(int i = 1; i < n; i++) {
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
scanf("%d", &Q);
for(int i = 1; i <= Q; i++) {
scanf("%d%d", &u, &v);
ask[u].push_back(v);
pos[u].push_back(i);
ask[v].push_back(u);
pos[v].push_back(-i);
}
tarjan(1);
for(int i = 1; i <= Q; i++) printf("%d\n", ans[i]);
return 0;
}