1. 程式人生 > 實用技巧 >「題解」poj 3728 The merchant

「題解」poj 3728 The merchant

題目

The merchant

簡化題意

給你一棵樹,點有點權,到達一個點你可以花費該點的點權買入一個東西,然後在另一個點把這個東西賣出(賣出的時候手上必須有東西),只能買入賣出一次,問你從一個點 \(u\) 到一個點 \(v\) 的路徑上能獲得的最大收益。

思路

倍增。
除了正常倍增需要維護的東西還要維護下面這些:
\(maxx[i][j]\),表示在 \(i\) 到從 \(i\)\(2 ^ j\) 步到達的那個點之間的路徑上的最大點權。
\(minn[i][j]\),表示在 \(i\) 到從 \(i\)\(2 ^ j\) 步到達的那個點之間的路徑上的最小點權。
\(up[i][j]\)

,表示在 \(i\) 到從 \(i\)\(2 ^ j\) 步到達的那個點之間的路徑上按 \(i \rightarrow 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;
}