黑恶势力向你招手~~
可以很快看出是LCA的题,但会发现此题涉及位置,不是简单的max-min。
可以发现分三种情况讨论:在u—LCA中买并卖;在LCA—v中买并卖;在u—LCA中买,在LCA—v中卖。
第一种情况需要min离u较近;第二种情况需要min离LCA较近;第三种就简单用max与min就行了。
然后,可用倍增求出。(好神奇)
上码!
#include<cstdio>
#include<vector>
using namespace std;
const int N = 50005, Inf = (1 << 30) - 1;
int n, w[N], T, d[N], dis[N][16], Limit, max1[N][16], min1[N][16], up[N][16], down[N][16];
vector <int> G[N];
void read(int &x) {
x = 0;
char s = getchar();
while(s > '9' || s < '0')
s = getchar();
while(s <= '9' && s >= '0') {
x = (x << 1) + (x << 3) + s - '0';
s = getchar();
}
}
int Log2(const float &x) {
return ((unsigned &) x >> 23 & 255) - 127;
}
int Max(const int x, const int y) {
return x > y ? x : y;
}
int Min(const int x, const int y) {
return x < y ? x : y;
}
void init(const int x, const int ba) {
d[x] = d[ba] + 1;
dis[x][0] = ba;
max1[x][0] = Max(w[x], w[ba]);//A
min1[x][0] = Min(w[x], w[ba]);
up[x][0] = Max(0, w[ba] - w[x]);
down[x][0] = Max(0, w[x] - w[ba]);
for(int i = 1; (1 << i) <= d[x]; i ++) {
dis[x][i] = dis[dis[x][i - 1]][i - 1];
max1[x][i] = Max(max1[x][i - 1], max1[dis[x][i - 1]][i - 1]);//B
min1[x][i] = Min(min1[x][i - 1], min1[dis[x][i - 1]][i - 1]);
up[x][i] = Max(Max(up[x][i - 1], up[dis[x][i - 1]][i - 1]), max1[dis[x][i - 1]][i - 1] - min1[x][i - 1]);
down[x][i] = Max(Max(down[x][i - 1], down[dis[x][i - 1]][i - 1]), max1[x][i - 1] - min1[dis[x][i - 1]][i - 1]);
}
for(int i = 0; i < G[x].size(); i ++) {
if(ba == G[x][i])
continue;
init(G[x][i], x);
}
}
void Swap(int &x, int &y) {
int t = x;
x = y;
y = t;
}
int lca(int x, int y) {
if(d[x] > d[y])
Swap(x, y);
int gap = d[y] - d[x];
for(int i = 0; (1 << i) <= gap; i ++)
if((1 << i) & gap)
y = dis[y][i];
if(x == y)
return x;
for(int i = Limit; i >= 0; i --)
if(dis[x][i] != dis[y][i]) {
x = dis[x][i];
y = dis[y][i];
}
return dis[x][0];
}
int Aup(int x, const int gap, int &mi) {
int res = 0;
mi = Inf;
for(int i = 0; (1 << i) <= gap; i ++)
if((1 << i) & gap) {
res = Max(res, Max(max1[x][i] - mi, up[x][i]));//C
mi = Min(mi, min1[x][i]);
x = dis[x][i];
}
return res;
}
int Adown(int x, const int gap, int &ma) {
int res = 0;
ma = 0;
for(int i = 0; (1 << i) <= gap; i ++)
if((1 << i) & gap) {
res = Max(res, Max(ma - min1[x][i], down[x][i]));
ma = Max(ma, max1[x][i]);
x = dis[x][i];
}
return res;
}
int main() {
int a, b, LCA, q, p;
read(n);
Limit = Log2(n);
for(int i = 1; i <= n; i ++)
read(w[i]);
for(int i = 1; i < n; i ++) {
read(a);
read(b);
G[a].push_back(b);
G[b].push_back(a);
}
init(1, 0);
read(T);
while(T --) {
read(a);
read(b);
LCA = lca(a, b);
printf("%d\n", Max(Max(Aup(a, d[a] - d[LCA], q), Adown(b, d[b] - d[LCA], p)), p - q));
}
return 0;
}
这题没什么坑,唯一的就是——不要输出负数哇咔咔!
注
A:因为表示u至其父节点,所以直接赋值。
B:我理解的是二分,这样就不难理解了。至于up与down,除了二分,还要思考另一种情况(在前一段买,在后一段卖)。
C:res在mi的前面,因为要先买才能卖嘛。而此时便表示这一段最大减去之前最小与这一段本身的最值比较。(Adown函数同理)
如果有不对的地方,请大佬在评论区指出哦。