「题解」洛谷 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;
}

浙公网安备 33010602011771号