【学习笔记】树链剖分

NCC79601

2019-03-13 23:26:15

Personal

树链剖分,就是指**把树给剖成链,然后用数据结构进行树上的区间操作。** 比如在这份笔记中,使用树链剖分+线段树维护([P3384](https://www.luogu.org/problemnew/show/P3384))。 定义:一个节点$u$所有儿子中**拥有子节点最多的儿子**$v$**就是重儿子**,由$u$到$v$这条链就是重链。找到所有的重链后,就把树按照重儿子在前,轻儿子按照 dfn 的顺序投影到数据结构上的话,那么这棵树就变得类似一个区间了,维护起来也方便很多。 # 树链剖分 树链剖分要用到两次 dfs 。 ### 第一次 第一次 dfs 需要记录父亲、深度、子树大小等信息,并且**按照儿子的子树大小顺序找到重儿子**。 **注意!** 在更新重儿子的时候,千万要写$size[v] > size[son[u]]$,否则找到的根本就**不是重儿子**,整份代码也就是个假的树链剖分,当时就是因为这个地方写飘了,导致 TLE 了三个点。 ```cpp inline void dfs1(int u, int fa) { f[u] = fa, depth[u] = depth[fa] + 1; size[u] = 1; for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == fa) continue; dfs1(v, u); size[u] += size[v]; if(size[v] > size[son[u]]) //attention! son[u] = v; } return; } ``` ### 第二次 第二次 dfs 则就是拆树过程。先记录当前节点所在链的头节点$top[u]$(方便 LCA ),并且记录 dfn 序。注意:这里的 dfn 序则就是**当前节点在新树中对应的新编号**,线段树也是对这东西进行维护。但是为了方便访问,也要记录下 dfn 所对应的原数字编号$rev[cnt]$。此后,优先将当前链向下延展,然后对于所有轻儿子进行 dfs 。 ```cpp inline void dfs2(int u, int t) { top[u] = t; dfn[u] = ++cnt; rev[cnt] = u; if(!son[u]) return; dfs2(son[u], t); for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == f[u] || v == son[u]) continue; dfs2(v, v); } return; } ``` # 数据结构维护 线段树维护不必多说,不过建树的时候注意当$l=r$的时候,要用原数值,即$v(num) = array[rev[l]]$,否则就$gg$了。 ```cpp inline void build_tree(int l, int r, int num) { tag(num) = 0; l(num) = l, r(num) = r; if(l == r) { v(num) = array[rev[l]] % p; return; } int mid = (l + r) >> 1; build_tree(l, mid, num<<1); build_tree(mid+1, r, num<<1|1); v(num) = (v(num<<1) + v(num<<1|1)) % p; return; } ``` # 树上修改及查询 在原树上进行操作,等价于在$[u,v]$最短路中所有被包含的链进行操作。由于链被分成了一段一段的,想要精确找到这些链则是个难题。 这里用了 LCA ,不过不是倍增 LCA 。由于之前已经记录下来了父亲$f[u]$以及所在链的链头$top[u]$,所以根本不需要倍增,直接逮着开跑就行。 左右两侧节点的处理方式完全一致,以$x$节点的处理为例: 维护一个$\_x$表示当前$x$节点所在链的链头,当前处理的区间也就是$[dfn[\_x],dfn[x]]$。如果这个链头的深度比$\_y$大,说明这条链在$y$那条链下面,所以可以直接对当前区间进行数据结构操作,然后把$x$跳到$\_x$的父亲,再把$\_x$更新为新的$x$所对应的链头。 但这样子处理有个问题,那就是当$\_x$与$\_y$相同后,$x$与$y$不见得相同。因此在$while$循环外**还要再进行一次维护**,直接对$dfn[x]$与$dfn[y]$之间的区间进行操作即可。 这个样子就完成了树上操作,查询区间和以及修改区间都可以用这个方法实现。 ```cpp inline long long summary(int &x, int &y) { long long sum = 0; int _x = top[x], _y = top[y]; while(_x != _y) { if(depth[_x] >= depth[_y]) { sum = (sum + query(dfn[_x], dfn[x], 1)) % p; x = f[_x], _x = top[x]; } else { sum = (sum + query(dfn[_y], dfn[y], 1)) % p; y = f[_y], _y = top[y]; } } if(dfn[x] <= dfn[y]) sum = (sum + query(dfn[x], dfn[y], 1)) % p; else sum = (sum + query(dfn[y], dfn[x], 1)) % p; return sum; } inline void update(int &x, int &y, long long c) { int _x = top[x], _y = top[y]; while(_x != _y) { if(depth[_x] >= depth[_y]) { edit_tree(dfn[_x], dfn[x], 1, c); x = f[_x], _x = top[x]; } else { edit_tree(dfn[_y], dfn[y], 1, c); y = f[_y], _y = top[y]; } } if(dfn[x] <= dfn[y]) edit_tree(dfn[x], dfn[y], 1, c); else edit_tree(dfn[y], dfn[x], 1, c); return; } ``` # 主程序 主程序里头先$dfs1$,再从$root$进行$dfs2$,最后建树即可。 ```cpp int main() { dfs1(r, 0); dfs2(r, r); build_tree(1, n, 1); } ``` --- 完整代码: ```cpp #include<bits/stdc++.h> #define l(x) segtree[x].l #define r(x) segtree[x].r #define v(x) segtree[x].val #define tag(x) segtree[x].tag using namespace std; const int MAXN = 100010; struct EDGE { int to, next; } edge[MAXN << 1]; int last[MAXN], id = 0; struct SEGTREE { long long l, r, val, tag; } segtree[MAXN*4]; long long array[MAXN]; int f[MAXN], depth[MAXN], son[MAXN], size[MAXN]; int top[MAXN], rev[MAXN], dfn[MAXN], cnt = 0; int n, m, p, r, a, b, opt; long long k; //树链剖分过程 inline void build_edge(int u, int v) { edge[++id].to = v; edge[id].next = last[u]; last[u] = id; return; } inline void dfs1(int u, int fa) { f[u] = fa, depth[u] = depth[fa] + 1; size[u] = 1; for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == fa) continue; dfs1(v, u); size[u] += size[v]; if(size[v] > size[son[u]]) //attention! son[u] = v; } return; } inline void dfs2(int u, int t) { top[u] = t; dfn[u] = ++cnt; rev[cnt] = u; if(!son[u]) return; dfs2(son[u], t); for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == f[u] || v == son[u]) continue; dfs2(v, v); } return; } //线段树维护 inline void build_tree(int l, int r, int num) { tag(num) = 0; l(num) = l, r(num) = r; if(l == r) { v(num) = array[rev[l]] % p; return; } int mid = (l + r) >> 1; build_tree(l, mid, num<<1); build_tree(mid+1, r, num<<1|1); v(num) = (v(num<<1) + v(num<<1|1)) % p; return; } inline void pushdown(int num) { if(!tag(num)) return; v(num<<1) = (v(num<<1) + tag(num) * (r(num<<1) - l(num<<1) + 1)) % p; v(num<<1|1) = (v(num<<1|1) + tag(num) * (r(num<<1|1) - l(num<<1|1) + 1)) % p; tag(num<<1) = (tag(num<<1) + tag(num)) % p; tag(num<<1|1) = (tag(num<<1|1) + tag(num)) % p; tag(num) = 0; return; } inline void edit_tree(int l, int r, int num, long long c) { if(l <= l(num) && r(num) <= r) { v(num) = (v(num) + c * (r(num) - l(num) + 1)) % p; tag(num) = (tag(num) + c) % p; return; } pushdown(num); int mid = (l(num) + r(num)) >> 1; if(l <= mid) edit_tree(l, r, num<<1, c); if(r > mid) edit_tree(l, r, num<<1|1, c); v(num) = (v(num<<1) + v(num<<1|1)) % p; return; } inline long long query(int l, int r, int num) { if(l <= l(num) && r(num) <= r) return v(num); pushdown(num); int mid = (l(num) + r(num)) >> 1; long long sum = 0; if(l <= mid) sum = (sum + query(l, r, num<<1)) % p; if(r > mid) sum = (sum + query(l, r, num<<1|1)) % p; return sum; } inline long long summary(int &x, int &y) { long long sum = 0; int _x = top[x], _y = top[y]; while(_x != _y) { if(depth[_x] >= depth[_y]) { sum = (sum + query(dfn[_x], dfn[x], 1)) % p; x = f[_x], _x = top[x]; } else { sum = (sum + query(dfn[_y], dfn[y], 1)) % p; y = f[_y], _y = top[y]; } } if(dfn[x] <= dfn[y]) sum = (sum + query(dfn[x], dfn[y], 1)) % p; else sum = (sum + query(dfn[y], dfn[x], 1)) % p; return sum; } inline void update(int &x, int &y, long long c) { int _x = top[x], _y = top[y]; while(_x != _y) { if(depth[_x] >= depth[_y]) { edit_tree(dfn[_x], dfn[x], 1, c); x = f[_x], _x = top[x]; } else { edit_tree(dfn[_y], dfn[y], 1, c); y = f[_y], _y = top[y]; } } if(dfn[x] <= dfn[y]) edit_tree(dfn[x], dfn[y], 1, c); else edit_tree(dfn[y], dfn[x], 1, c); return; } int main() { scanf("%d%d%d%d", &n, &m, &r, &p); for(register int i = 1; i <= n; i++) { scanf("%lli", &array[i]); array[i] %= p; } for(register int i = 1; i < n; i++) { scanf("%d%d", &a, &b); build_edge(a, b); build_edge(b, a); } dfs1(r, 0); dfs2(r, r); build_tree(1, n, 1); for(register int i = 1; i <= m; i++) { scanf("%d", &opt); switch(opt) { case 1: { scanf("%d%d%lli", &a, &b, &k); update(a, b, k); break; } case 2: { scanf("%d%d", &a, &b); printf("%lli\n", summary(a, b)); break; } case 3: { scanf("%d%lli", &a, &k); edit_tree(dfn[a], dfn[a]+size[a]-1, 1, k); break; } case 4: { scanf("%d", &a); printf("%lli\n", query(dfn[a], dfn[a]+size[a]-1, 1)); break; } } } return 0; } ``` --- 如何用树链剖分+线段树维护**树上路径颜色段数量**呢?这里记录下一个骚气的做法,线段树还可以这样用! ## 一道比较好的树剖题 [P2146](https://www.luogu.org/problemnew/show/P2146) ```cpp #include<bits/stdc++.h> using namespace std; const int MAXN = 100010; int fa[MAXN], depth[MAXN], dfn[MAXN], rev[MAXN]; int top[MAXN], size[MAXN], son[MAXN], cnt = 0; int n, q; struct SEGTREE { int l, r, len; long long tag, v; } tree[MAXN << 2]; struct EDGE { int to, next; } edge[MAXN << 1]; int id = 0, last[MAXN]; inline int read() { int res = 0, uz = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') uz = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { res = (res << 3) + (res << 1) + (ch ^ '0'); ch = getchar(); } return res * uz; } inline void build_edge(int u, int v) { edge[++id].to = v; edge[id].next = last[u]; last[u] = id; return; } inline void dfs1(int u, int father) { fa[u] = father, depth[u] = depth[father] + 1; size[u] = 1; for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == father) continue; dfs1(v, u); size[u] += size[v]; if(size[v] > size[son[u]]) son[u] = v; } return; } inline void dfs2(int u, int t) { top[u] = t; dfn[u] = ++cnt; rev[cnt] = u; if(!son[u]) return; dfs2(son[u], t); for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == fa[u] || v == son[u]) continue; dfs2(v, v); } return; } inline void pushdown(int x) { if(!~tree[x].tag) return; tree[x<<1].v = tree[x].tag * tree[x<<1].len; tree[x<<1|1].v = tree[x].tag * tree[x<<1|1].len; tree[x<<1].tag = tree[x<<1|1].tag = tree[x].tag; tree[x].tag = -1; return; } inline void build_tree(int x, int l, int r) { tree[x].len = r - l + 1; tree[x].tag = -1, tree[x].v = 0; tree[x].l = l, tree[x].r = r; if(l == r) return; int mid = (l + r) >> 1; build_tree(x<<1, l, mid); build_tree(x<<1|1, mid+1, r); return; } inline long long query(int l, int r, int x) { int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) return tree[x].v; pushdown(x); int mid = (ll + rr) >> 1; long long res = 0; if(l <= mid) res += query(l, r, x<<1); if(r > mid) res += query(l, r, x<<1|1); return res; } inline void edit_tree(int l, int r, int x, int v) { int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) { tree[x].v = v * tree[x].len; tree[x].tag = v; return; } pushdown(x); int mid = (ll + rr) >> 1; if(l <= mid) edit_tree(l, r, x<<1, v); if(r > mid) edit_tree(l, r, x<<1|1, v); tree[x].v = tree[x<<1].v + tree[x<<1|1].v; return; } inline void update(int u, int v, int val) { while(top[u] != top[v]) { if(depth[top[u]] < depth[top[v]]) swap(u, v); edit_tree(dfn[top[u]], dfn[u], 1, val); u = fa[top[u]]; } if(dfn[u] > dfn[v]) swap(u, v); edit_tree(dfn[u], dfn[v], 1, val); return; } int main() { n = read(); for(register int i = 2; i <= n; i++) { int depend = read() + 1; build_edge(depend, i); } dfs1(1, 1); dfs2(1, 1); build_tree(1, 1, cnt); char s[20]; q = read(); while(q--) { scanf("%s", s); int app = read() + 1; int pre = tree[1].v; if(s[0] == 'i') { update(1, app, 1); printf("%lli\n", abs(tree[1].v - pre)); } if(s[0] == 'u') { edit_tree(dfn[app], dfn[app] + size[app] - 1, 1, 0); printf("%lli\n", abs(tree[1].v - pre)); } } return 0; } ``` --- 还有类似对边权处理、对区间取相反数的问题,都可以用树链剖分解决,可以把边权全部投影到**后向点**(边两端深度最大的点)上,处理一段路径的时候注意**不能把 LCA 给处理了,否则直接$gg$**。 另外,取相反数的$lazytag$在下放、修改时有**注意事项**,相当于是$tree[x].tag\otimes1\,($取**异或**$)$,具体直接看代码。 ## 例题 [P1505](https://www.luogu.org/problemnew/show/P1505) ```cpp #include<bits/stdc++.h> using namespace std; const int MAXN = 1000010; const int INF = 1 << 30; struct EDGE { int to, next, val; } edge[MAXN << 1]; int last[MAXN], id = 0; struct SEGTREE { int l, r, tag; int v, max, min; } tree[MAXN << 2]; inline int read() { int res = 0, uz = 1; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') uz = -1; ch = getchar(); } while(isdigit(ch)) { res = (res << 3) + (res << 1) + (ch ^ '0'); ch = getchar(); } return res * uz; } inline void build_edge(int u, int v, int val) { edge[++id].to = v; edge[id].val = val; edge[id].next = last[u]; last[u] = id; return; } int w[MAXN]; int size[MAXN], son[MAXN], depth[MAXN], fa[MAXN]; int top[MAXN], dfn[MAXN], rev[MAXN], cnt = 0; int n; inline void dfs1(int u, int father) { fa[u] = father, depth[u] = depth[father] + 1; size[u] = 1; for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == father) continue; w[v] = edge[i].val; dfs1(v, u); size[u] += size[v]; if(size[v] > size[son[u]]) son[u] = v; } return; } inline void dfs2(int u, int t) { dfn[u] = ++cnt, rev[cnt] = u; top[u] = t; if(!son[u]) return; dfs2(son[u], t); for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == fa[u] || v == son[u]) continue; dfs2(v, v); } return; } inline void pushdown(int x) { if(!tree[x].tag) return; //left tree[x<<1].tag += tree[x].tag; tree[x<<1].tag %= 2; //^=1 tree[x<<1].v *= -1; swap(tree[x<<1].max, tree[x<<1].min); tree[x<<1].max *= -1, tree[x<<1].min *= -1; //right tree[x<<1|1].tag += tree[x].tag; tree[x<<1|1].tag %= 2; //^=1 tree[x<<1|1].v *= -1; swap(tree[x<<1|1].max, tree[x<<1|1].min); tree[x<<1|1].max *= -1, tree[x<<1|1].min *= -1; tree[x].tag = 0; return; } inline void pushup(int x) { tree[x].v = tree[x<<1].v + tree[x<<1|1].v; tree[x].max = max(tree[x<<1].max, tree[x<<1|1].max); tree[x].min = min(tree[x<<1].min, tree[x<<1|1].min); return; } inline void build_tree(int x, int l, int r) { //printf("build %d->[%d,%d]\n", x, l, r); tree[x].l = l, tree[x].r = r; tree[x].tag = 0; if(l == r) { tree[x].v = tree[x].max = tree[x].min = w[rev[l]]; return; } int mid = (l + r) >> 1; build_tree(x<<1, l, mid); build_tree(x<<1|1, mid+1, r); pushup(x); return; } inline void edit_tree(int x, int p, int c) { //printf("edit_tree p:%d now:%d->[%d,%d]->%d\n", p, x, tree[x].l, tree[x].r, c); if(tree[x].l == tree[x].r) { tree[x].v = tree[x].max = tree[x].min = c; return; } pushdown(x); int mid = (tree[x].l + tree[x].r) >> 1; if(p <= mid) edit_tree(x<<1, p, c); else edit_tree(x<<1|1, p, c); pushup(x); return; } inline void reverse_tree(int x, int l, int r) { int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) { tree[x].tag ^= 1; tree[x].v *= -1; swap(tree[x].max, tree[x].min); tree[x].max *= -1, tree[x].min *= -1; return; } pushdown(x); int mid = (ll + rr) >> 1; if(l <= mid) reverse_tree(x<<1, l, r); if(r > mid) reverse_tree(x<<1|1, l, r); pushup(x); return; } inline int query_sum(int x, int l, int r) { //printf("querysum %d->[%d,%d]\n", x, l, r); int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) return tree[x].v; pushdown(x); int mid = (ll + rr) >> 1; int res = 0; if(l <= mid) res += query_sum(x<<1, l, r); if(r > mid) res += query_sum(x<<1|1, l, r); return res; } inline int query_max(int x, int l, int r) { int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) return tree[x].max; pushdown(x); int res = -INF; int mid = (ll + rr) >> 1; if(l <= mid) res = max(res, query_max(x<<1, l, r)); if(r > mid) res = max(res, query_max(x<<1|1, l, r)); return res; } inline int query_min(int x, int l, int r) { int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) return tree[x].min; pushdown(x); int res = INF; int mid = (ll + rr) >> 1; if(l <= mid) res = min(res, query_min(x<<1, l, r)); if(r > mid) res = min(res, query_min(x<<1|1, l, r)); return res; } inline int Sum(int u, int v) { int res = 0; while(top[u] != top[v]) { if(depth[top[u]] < depth[top[v]]) swap(u, v); res += query_sum(1, dfn[top[u]], dfn[u]); u = fa[top[u]]; } if(dfn[u] > dfn[v]) swap(u, v); res += query_sum(1, dfn[u]+1, dfn[v]); return res; } inline void Reverse(int u, int v) { while(top[u] != top[v]) { if(depth[top[u]] < depth[top[v]]) swap(u, v); reverse_tree(1, dfn[top[u]], dfn[u]); u = fa[top[u]]; } if(dfn[u] > dfn[v]) swap(u, v); reverse_tree(1, dfn[u]+1, dfn[v]); return; } inline int Max(int u, int v) { int res = -INF; while(top[u] != top[v]) { if(depth[top[u]] < depth[top[v]]) swap(u, v); res = max(res, query_max(1, dfn[top[u]], dfn[u])); u = fa[top[u]]; } if(dfn[u] > dfn[v]) swap(u, v); res = max(res, query_max(1, dfn[u]+1, dfn[v])); return res; } inline int Min(int u, int v) { int res = INF; while(top[u] != top[v]) { if(depth[top[u]] < depth[top[v]]) swap(u, v); res = min(res, query_min(1, dfn[top[u]], dfn[u])); u = fa[top[u]]; } if(dfn[u] > dfn[v]) swap(u, v); res = min(res, query_min(1, dfn[u]+1, dfn[v])); return res; } int main() { n = read(); for(register int i = 1; i < n; i++) { int u, v, w; u = read() + 1, v = read() + 1, w = read(); build_edge(u, v, w); build_edge(v, u, w); } dfs1(1, 1); dfs2(1, 1); build_tree(1, 1, n); int m = read(); while(m--) { char s[10]; int a, b; scanf("%s", s); a = read(); b = read(); if(s[0] == 'C') edit_tree(1, dfn[++a], b); else if(s[0] == 'N') Reverse(++a, ++b); else if(s[0] == 'S') printf("%d\n", Sum(++a, ++b)); else if(s[1] == 'A') printf("%d\n", Max(++a, ++b)); else printf("%d\n", Min(++a, ++b)); } return 0; } ``` --- ## 例题 [P4315](https://www.luogu.org/problemnew/show/P4315) 这道题的核心就是如何正确地打 **[线段树](https://ncc79601.blog.luogu.org/post-xue-xi-bi-ji-xian-duan-shu)** ,以及如何处理**单独修改边权**的问题。 **修改第$\textbf{u}$条边时,加上这句话:** ```cpp u = depth[edge[(u<<1)-1].to] < depth[edge[u<<1].to] ? edge[u<<1].to : edge[(u<<1)-1].to; ``` 意思是说,判断当前这条边的边权投影到的是哪个点。由于输入的$u$是表示第$u$条边,由于我们存的是无向边,所以$2u-1$、$2u$编号的边都是“第$u$条边”,由于投影时是将边权保存到边的后向点,所以只需要找到**深度最大的那个点**即可。 --- 完整代码 ```cpp #include<bits/stdc++.h> using namespace std; const int MAXN = 100010; const int INF = 1 << 30; struct EDGE { int to, next; int val; } edge[MAXN]; int last[MAXN], id = 0; struct SEGTREE { int l, r; int tag, c, max; } tree[MAXN << 2]; inline void build_edge(int u, int v, int val) { edge[++id].to = v; edge[id].val = val; edge[id].next = last[u]; last[u] = id; return; } int n, w[MAXN]; int fa[MAXN], depth[MAXN], size[MAXN], son[MAXN]; int top[MAXN], dfn[MAXN], rev[MAXN], cnt = 0; inline void dfs1(int u, int father) { fa[u] = father, depth[u] = depth[father] + 1; size[u] = 1; for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == father) continue; w[v] = edge[i].val; dfs1(v, u); size[u] += size[v]; if(size[v] > size[son[u]]) son[u] = v; } return; } inline void dfs2(int u, int t) { top[u] = t; dfn[u] = ++cnt, rev[cnt] = u; if(!son[u]) return; dfs2(son[u], t); for(register int i = last[u]; i; i = edge[i].next) { int v = edge[i].to; if(v == fa[u] || v == son[u]) continue; dfs2(v, v); } return; } inline void pushdown(int x) { if(~tree[x].c) { tree[x<<1].max = tree[x<<1|1].max = tree[x].c; tree[x<<1].c = tree[x<<1|1].c = tree[x].c; tree[x<<1].tag = tree[x<<1|1].tag = 0; tree[x].c = -1; } if(tree[x].tag) { tree[x<<1].max += tree[x].tag; tree[x<<1|1].max += tree[x].tag; tree[x<<1].tag += tree[x].tag; tree[x<<1|1].tag += tree[x].tag; tree[x].tag = 0; } return; } inline void pushup(int x) { tree[x].max = max(tree[x<<1].max, tree[x<<1|1].max); return; } inline void build_tree(int x, int l, int r) { tree[x].l = l, tree[x].r = r; tree[x].tag = 0, tree[x].c = -1; if(l == r) { tree[x].max = w[rev[l]]; return; } int mid = (l + r) >> 1; build_tree(x<<1, l, mid); build_tree(x<<1|1, mid + 1, r); pushup(x); return; } inline void cover(int x, int l, int r, int c) { int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) { tree[x].max = tree[x].c = c; tree[x].tag = 0; return; } pushdown(x); int mid = (ll + rr) >> 1; if(l <= mid) cover(x<<1, l, r, c); if(r > mid) cover(x<<1|1, l, r, c); pushup(x); return; } inline void add(int x, int l, int r, int c) { int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) { tree[x].max += c; tree[x].tag += c; return; } pushdown(x); int mid = (ll + rr) >> 1; if(l <= mid) add(x<<1, l, r, c); if(r > mid) add(x<<1|1, l, r, c); pushup(x); return; } inline int query(int x, int l, int r) { int res = -INF; int ll = tree[x].l, rr = tree[x].r; if(l <= ll && rr <= r) return tree[x].max; pushdown(x); int mid = (ll + rr) >> 1; if(l <= mid) res = max(res, query(x<<1, l, r)); if(r > mid) res = max(res, query(x<<1|1, l, r)); return res; } inline void Cover(int u, int v, int c) { while(top[u] != top[v]) { if(depth[top[u]] < depth[top[v]]) swap(u, v); cover(1, dfn[top[u]], dfn[u], c); u = fa[top[u]]; } if(dfn[u] > dfn[v]) swap(u, v); cover(1, dfn[u] + 1, dfn[v], c); return; } inline void Add(int u, int v, int c) { while(top[u] != top[v]) { if(depth[top[u]] < depth[top[v]]) swap(u, v); add(1, dfn[top[u]], dfn[u], c); u = fa[top[u]]; } if(dfn[u] > dfn[v]) swap(u, v); add(1, dfn[u] + 1, dfn[v], c); return; } inline int Max(int u, int v) { int res = -INF; while(top[u] != top[v]) { if(depth[top[u]] < depth[top[v]]) swap(u, v); res = max(res, query(1, dfn[top[u]], dfn[u])); u = fa[top[u]]; } if(dfn[u] > dfn[v]) swap(u, v); res = max(res, query(1, dfn[u] + 1, dfn[v])); return res; } int main() { scanf("%d", &n); for(register int i = 1; i < n; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); build_edge(u, v, w); build_edge(v, u, w); } dfs1(1, 1); dfs2(1, 1); build_tree(1, 1, n); char s[10]; scanf("%s", s); while(s[0] != 'S') { switch(s[1]) { case 'a': { int u, v; scanf("%d%d", &u, &v); printf("%d\n", Max(u, v)); break; } case 'o': { int u, v, c; scanf("%d%d%d", &u, &v, &c); Cover(u, v, c); break; } case 'd': { int u, v, c; scanf("%d%d%d", &u, &v, &c); Add(u, v, c); break; } case 'h': { int u, c; scanf("%d%d", &u, &c); u = depth[edge[(u<<1)-1].to] < depth[edge[u<<1].to] ? edge[u<<1].to : edge[(u<<1)-1].to; cover(1, dfn[u], dfn[u], c); break; } } scanf("%s", s); } return 0; } ```