「题解」洛谷 P3976 旅游

描述

题目传送门

思路

发现一条链上利润最大值可以用线段树维护,具体地,每个结点维护区间从左端点走到右端点的最大利润 \(\text{sumlr}_u\),从右端点走到左端点的最大利润 \(\text{sumrl}_u\),最大价格 \(\text{mx}_u\),最小价格 \(\text{mn}_u\)。转移时,\(\text{sumlr}_u=\max\{\text{sumlr}_{\text{lson}},\text{sumlr}_{\text{rson}},\text{mx}_{\text{rson}}-\text{mn}_{\text{lson}}\}\)\(\text{sumrl}_u=\max\{\text{sumrl}_{\text{lson}},\text{sumrl}_{\text{rson}},\text{mx}_{\text{lson}}-\text{mn}_{\text{rson}}\}\)

于是想到树剖,记询问两点依次为 \(u,v\)\(u\)\(v\) 的最近公共祖先为 \(\text{lca}\)\(\text{lca}\) 可以通过倍增或者树剖得到。记录 \(u\)\(\text{lca}\) 的路径上所有重链的询问结果, \(v\)\(\text{lca}\) 下面那个结点的路径上所有重链的询问结果。按照线段树的转移方式依次合并即可。关于 \(\text{lca}\) 下面那个结点,可以倍增来求。

代码

#include <bits/stdc++.h>
using namespace std;
#define MAXN 50010
#define LOGN 22
int n, q, a[MAXN], fa[MAXN], anc[MAXN][LOGN], dis[MAXN], siz[MAXN], son[MAXN], tim, dfn[MAXN], top[MAXN], val[MAXN];
vector<int> g[MAXN];
inline void init(const int &u, const int &f) {
	siz[u] = 1;
	for (register int i = 0; i < g[u].size(); i++) {
		const int v = g[u][i];
		if (v == f) continue;
		fa[v] = u;
		anc[v][0] = u; 
		dis[v] = dis[u] + 1;
		init(v, u);
		siz[u] += siz[v];
		if (siz[son[u]] < siz[v]) son[u] = v;
	}
}
inline void dfs(const int &u, const int &p) {
	top[u] = p;
	dfn[u] = ++tim; val[tim] = u;
	if (!son[u]) return;
	dfs(son[u], p);
	for (register int i = 0; i < g[u].size(); i++) {
		const int v = g[u][i];
		if (v == fa[u] || v == son[u]) continue;
		dfs(v, v);
	}
}
struct Segment_Tree {
	int mx[MAXN << 2], mn[MAXN << 2], sumlr[MAXN << 2], sumrl[MAXN << 2], lzy[MAXN << 2];
	inline bool InRange(const int &l, const int &r, const int &L, const int &R) { return L <= l && R >= r; }
	inline bool OutoRange(const int &l, const int &r, const int &L, const int &R) { return r < L || R < l; }
	inline int merge(const int &fl, const int &fr, const int &mxr, const int &mnl) { return max(fl, max(fr, mxr - mnl)); }
	inline void pushup(const int &u) {
		mx[u] = max(mx[u << 1], mx[u << 1 | 1]);
		mn[u] = min(mn[u << 1], mn[u << 1 | 1]);
		sumlr[u] = max(-1000000000, merge(sumlr[u << 1], sumlr[u << 1 | 1], mx[u << 1 | 1], mn[u << 1]));
		sumrl[u] = max(-1000000000, merge(sumrl[u << 1], sumrl[u << 1 | 1], mx[u << 1], mn[u << 1 | 1]));
	}
	inline void maketag(const int &u, const int &w) {
		mx[u] += w; mn[u] += w;
		lzy[u] += w;
	}
	inline void pushdown(const int &u) {
		if (!lzy[u]) return;
		maketag(u << 1, lzy[u]); maketag(u << 1 | 1, lzy[u]);
		lzy[u] = 0;
	}
	inline void build(const int &u, const int &l, const int &r) {
		mx[u] = -1e9, mn[u] = 1e9, sumlr[u] = sumrl[u] = -1e9;
		if (l == r) {
			mx[u] = mn[u] = a[val[l]];
			return;
		}
		const int mid = (l + r) >> 1;
		build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
		pushup(u);
	}
	inline void update(const int &u, const int &l, const int &r, const int &L, const int &R, const int &w) {
		if (InRange(l, r, L, R)) maketag(u, w);
		else if (!OutoRange(l, r, L, R)) {
			pushdown(u); 
			const int mid = (l + r) >> 1; update(u << 1, l, mid, L, R, w), update(u << 1 | 1, mid + 1, r, L, R, w);
			pushup(u);
		} 
	}
	inline pair<pair<int, int>, pair<int, int> > query(const int &u, const int &l, const int &r, const int &L, const int &R) {
		if (InRange(l, r, L, R)) return {{sumlr[u], sumrl[u]}, {mx[u], mn[u]}};
		else if (!OutoRange(l, r, L, R)) {
			pushdown(u);
			const int mid = (l + r) >> 1;
			if (R <= mid) return query(u << 1, l, mid, L, R);
			else if (L > mid) return query(u << 1 | 1, mid + 1, r, L, R);
			const pair<pair<int, int>, pair<int, int> > la = query(u << 1, l, mid, L, R), ra = query(u << 1 | 1, mid + 1, r, L, R);
			return {{merge(la.first.first, ra.first.first, ra.second.first, la.second.second), 
					  merge(la.first.second, ra.first.second, la.second.first, ra.second.second)}, 
					 {max(la.second.first, ra.second.first), min(la.second.second, ra.second.second)}};
		}
	}
} tree; 
inline int _lca(int u, int v) {
	while (top[u] != top[v]) {
		if (dis[top[u]] < dis[top[v]]) swap(u, v);
		u = fa[top[u]];
	}
	return (dis[u] < dis[v] ? u : v);
}
pair<pair<int, int>, pair<int, int> > tmp1[50010], tmp2[50010];
inline int query(int u, int v) {
	int lca = _lca(u, v), len1 = 0, len2 = 0;
	while (dis[top[u]] > dis[lca]) {
		tmp1[++len1] = tree.query(1, 1, n, dfn[top[u]], dfn[u]);
		u = fa[top[u]];
	}
	if (dfn[u] >= dfn[lca]) tmp1[++len1] = tree.query(1, 1, n, dfn[lca], dfn[u]);
	while (dis[top[v]] > dis[lca] + 1) {   // v 和 lca 下面那个点在同一个重链上 
		tmp2[++len2] = tree.query(1, 1, n, dfn[top[v]], dfn[v]);
		v = fa[top[v]];
	}
	if (dis[v] >= dis[lca] + 1) {
		int tmp = v;   // lca 下面那个点
		for (register int i = 20; i >= 0; i--)
			if (dis[anc[tmp][i]] >= dis[lca] + 1) tmp = anc[tmp][i]; 
		if (dfn[v] >= dfn[tmp]) tmp2[++len2] = tree.query(1, 1, n, dfn[tmp], dfn[v]);
	}
	int sum = -1e9, mx = -1e9, mn = 1e9;
	for (register int i = 1; i <= len1; i++) {
		swap(tmp1[i].first.first, tmp1[i].first.second);
		sum = tree.merge(sum, tmp1[i].first.first, tmp1[i].second.first, mn);
		mx = max(mx, tmp1[i].second.first);
		mn = min(mn, tmp1[i].second.second);
	}
	for (register int i = len2; i >= 1; i--) {
		sum = tree.merge(sum, tmp2[i].first.first, tmp2[i].second.first, mn);
		mx = max(mx, tmp2[i].second.first);
		mn = min(mn, tmp2[i].second.second);
	}
	return max(sum, 0);
}
inline void update(int u, int v, const int &w) {
	while (top[u] != top[v]) {
		if (dis[top[u]] < dis[top[v]]) swap(u, v);
		tree.update(1, 1, n, dfn[top[u]], dfn[u], w);
		u = fa[top[u]];
	}
	if (dfn[u] > dfn[v]) swap(u, v);
	tree.update(1, 1, n, dfn[u], dfn[v], w);
}
signed main() {
	cin >> n;
	for (register int i = 1; i <= n; i++) cin >> a[i];
	for (register int i = 1; i < n; i++) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v), g[v].push_back(u);
	}
	dis[1] = 1; init(1, 0); dfs(1, 1); tree.build(1, 1, n);
	for (register int j = 1; j <= 20; j++)
		for (register int i = 1; i <= n; i++)
			anc[i][j] = anc[anc[i][j - 1]][j - 1]; 
	cin >> q;
	while (q--) {
		int u, v, w;
		cin >> u >> v >> w;
		cout << query(u, v) << endl;
		update(u, v, w);
	}
	return 0;
} 
posted @ 2022-10-29 16:20  Network_Error  阅读(29)  评论(0)    收藏  举报