「題解」poj 3728 The merchant
阿新 • • 發佈:2020-08-26
題目
簡化題意
給你一棵樹,點有點權,到達一個點你可以花費該點的點權買入一個東西,然後在另一個點把這個東西賣出(賣出的時候手上必須有東西),只能買入賣出一次,問你從一個點 \(u\) 到一個點 \(v\) 的路徑上能獲得的最大收益。
思路
倍增。
除了正常倍增需要維護的東西還要維護下面這些:
\(maxx[i][j]\),表示在 \(i\) 到從 \(i\) 跳 \(2 ^ j\) 步到達的那個點之間的路徑上的最大點權。
\(minn[i][j]\),表示在 \(i\) 到從 \(i\) 跳 \(2 ^ j\) 步到達的那個點之間的路徑上的最小點權。
\(up[i][j]\)
\(down[i][j]\),表示在 \(i\) 到從 \(i\) 跳 \(2 ^ j\) 步到達的那個點之間的路徑上按 \(i ^ j \rightarrow i\) 的順序能獲得的最大收益。
對於 \(u \rightarrow v\),進行分類討論,如下圖。
\(t\) 是 \(u\) 和 \(v\) 的最近公共祖先。
- 可能在 \(u \rightarrow t\) 的路徑上獲得最大收益,即在 \(u \rightarrow t\)
- 可能在 \(t \rightarrow v\) 的路徑上獲得最大收益,即在 \(t \rightarrow v\) 上買賣。
- 可能在 \(u \rightarrow t\) 上買,在 \(t \rightarrow v\) 上賣。
Code
#include <cstdio> #include <cstring> #include <cstring> #include <iostream> #include <algorithm> #define MAXN 50001 #define inf 2147483647 int max(int a, int b) { return a > b ? a : b; } int min(int a, int b) { return a < b ? a : b; } int n, m, pthn, a[MAXN], head[MAXN]; int lg[MAXN], fa[MAXN][21], dep[MAXN]; int maxx[MAXN][21], minn[MAXN][21], up[MAXN][21], down[MAXN][21]; struct Edge { int next, to; }pth[MAXN << 1]; void add(int from, int to) { pth[++pthn].to = to, pth[pthn].next = head[from]; head[from] = pthn; } void dfs(int u, int father) { maxx[u][0] = max(a[u], a[father]); minn[u][0] = min(a[u], a[father]); up[u][0] = max(0, a[father] - a[u]); down[u][0] = max(0, a[u] - a[father]); fa[u][0] = father, dep[u] = dep[father] + 1; for (int i = head[u]; i; i = pth[i].next) { int x = pth[i].to; if (x != father) dfs(x, u); } } int lca(int x, int y) { if (dep[y] > dep[x]) std::swap(x, y); while (dep[x] > dep[y]) { x = fa[x][lg[dep[x] - dep[y]] - 1]; } if (x == y) return x; for (int k = lg[dep[x]] - 1; k >= 0; --k) { if (fa[x][k] != fa[y][k]) { x = fa[x][k]; y = fa[y][k]; } } return fa[x][0]; } int getup(int s, int t, int &min_) { int up_ = 0; while (dep[s] > dep[t]) { int step = lg[dep[s] - dep[t]] - 1; up_ = max(up_, max(up[s][step], maxx[s][step] - min_)); min_ = min(min_, minn[s][step]); s = fa[s][step]; } return up_; } int getdown(int s, int t, int &max_) { int down_ = 0; while (dep[s] > dep[t]) { int step = lg[dep[s] - dep[t]] - 1; down_ = max(down_, max(down[s][step], max_ - minn[s][step])); //std::cout << max_ << " " << s << " " << step << " " << maxx[s][step] << '\n'; max_ = max(max_, maxx[s][step]); s = fa[s][step]; //std::cout << max_ << " " << s << " " << step << " " << maxx[s][step] << '\n'; } return down_; } int main() { //freopen("a.txt", "w", stdout); scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%d", &a[i]); for (int i = 1, u, v; i < n; ++i) { scanf("%d %d", &u, &v); add(u, v), add(v, u); } for (int i = 1; i <= n; ++i) { lg[i] = lg[i - 1] + ((1 << lg[i - 1]) == i); } dfs(1, 0); for (int j = 1; (1 << j) <= n; ++j) { for (int i = 1; i <= n; ++i) { int temp = fa[i][j - 1]; fa[i][j] = fa[temp][j - 1]; maxx[i][j] = max(maxx[i][j - 1], maxx[temp][j - 1]); minn[i][j] = min(minn[i][j - 1], minn[temp][j - 1]); up[i][j] = max(max(up[i][j - 1], up[temp][j - 1]), maxx[temp][j - 1] - minn[i][j - 1]); down[i][j] = max(max(down[temp][j - 1], down[i][j - 1]), maxx[i][j - 1] - minn[temp][j - 1]); } } scanf("%d", &m); for (int i = 1, u, v; i <= m; ++i) { scanf("%d %d", &u, &v); int l = lca(u, v), min_ = inf, max_ = 0; int upmx = getup(u, l, min_); int downmx = getdown(v, l, max_); //std::cout << u << " " << v << " " << l << '\n'; //std::cout << upmx << " " << downmx << " " << min_ << " " << max_ << '\n'; int ans = max(max(upmx, downmx), max_ - min_); printf("%d\n", ans); } return 0; }