树链剖分 换根操作
三个操作:
-
修改根;
-
修改路径上的值;
-
查询以 x x x 为根的子树。
1 修改根:
用变量 r o o t root root 把它存下来即可,它主要是为操作 3 服务的。
在 d f s dfs dfs 的时候以 1 为根。
2 修改路径上的值:
基本上是常规操作,线段树存两个变量: l z lz lz 和 m i n n minn minn 。
m i n n minn minn 为 3 服务,不多说, l z lz lz 懒标记主要用于判断 p u s h d o w n pushdown pushdown ,所以值什么的随便赋就好,但一定要有。
3 查询子树最小值:
换根操作。
这个就是这道题的重头戏了。
把树看成一幅图,每次我们把 r o o t root root 作为根,把这个图提起来,然后求以 x x x 为根的子树的最小值。
这是抽象想法,但是我们在查询的时候是按照以 1 为根的子树查询的。
此时大致有 3 种情况:
-
x = = r o o t x == root x==root :这就是全局最小值。
-
x x x 与 r o o t root root 没有交集:直接正常查询就好。
-
x x x 在 1 到 r o o t root root 的链上:抽象想象一下先。如何判断?另写函数算 x 是 root 的几倍祖先或者通过他们的深度判断。如何计算?概述起来就是除了 x 到 root 那条链上的数不要,其他的都要。这里抽象想想可以想通,具体怎么写看代码。
精华代码(换根操作):
int gf (int x, int y)
{
for (int i = 25; i >= 0; i--)
{
if (y >= (1 << i))
{
x = f[x][i];
y -= (1 << i);
}
}
return x;
}
int rquery (int x)
{
if (x == root) return t[1].minn;
int y = gf (root, dep[root] - dep[x] - 1);
if (dep[x] >= dep[root] or f[y][0] != x) return query (1, id[x], id[x] + siz[x] - 1);
int aa, bb;
aa = query (1, 1, id[y] - 1);
bb = query (1, id[y] + siz[y], n);
return min (aa, bb);
}
感性理解,实在不行去看第一篇题解,就可以看懂了 (我在干什么)。
AC 代码 (这不压行我都对不起我自己啊)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 100005;
int dep[maxn], son[maxn], f[maxn][25], siz[maxn], val[maxn], valn[maxn], top[maxn], id[maxn], hd[maxn], cnt, tmp, n, m, ans, root;
struct node{int to, nxt;}e[maxn * 2];
struct tree{int l, r, lz, minn;}t[maxn * 4];
void add (int u, int v){e[++cnt].to = v, e[cnt].nxt = hd[u], hd[u] = cnt;}
void dfs1 (int u, int fa){
dep[u] = dep[fa] + 1, siz[u] = 1, f[u][0] = fa;
for (int i = 1; i <= 25; i++) {f[u][i] = f[f[u][i - 1]][i - 1]; if (!f[u][i]) break;}
int maxss = -1;
for (int i = hd[u]; i; i = e[i].nxt){
if (e[i].to == fa) continue;
dfs1 (e[i].to, u), siz[u] += siz[e[i].to];
if (siz[e[i].to] > maxss) maxss = siz[e[i].to], son[u] = e[i].to;}}
void dfs2 (int x, int tpx){
top[x] = tpx, id[x] = ++tmp, valn[id[x]] = val[x];
if (!son[x]) return;
dfs2 (son[x], tpx);
for (int i = hd[x]; i; i = e[i].nxt){
if (e[i].to == son[x] or e[i].to == f[x][0]) continue;
dfs2 (e[i].to, e[i].to);}}
void push_down (int i){if (!t[i].lz) return; t[i << 1].lz = t[i << 1 | 1].lz = t[i].lz, t[i << 1].minn = t[i << 1 | 1].minn = t[i].minn, t[i].lz = 0;}
void build (int i, int l, int r){
t[i].l = l, t[i].r = r;
if (l == r) {t[i].minn = valn[l]; return;}
int mid = (l + r) >> 1;
build (i << 1, l, mid), build (i << 1 | 1, mid + 1, r);
t[i].minn = min (t[i << 1].minn, t[i << 1 | 1].minn);}
void update (int i, int l, int r, int x){
if (t[i].l > r or t[i].r < l) return;
if (t[i].l >= l and t[i].r <= r){t[i].minn = x, t[i].lz = 1; return;}
push_down (i);
update (i << 1, l, r, x), update (i << 1 | 1, l, r, x);
t[i].minn = min (t[i << 1].minn, t[i << 1 | 1].minn); }
int query (int i, int l, int r){
if (t[i].l > r or t[i].r < l) return 1e18;
if (t[i].l >= l and t[i].r <= r) return t[i].minn;
push_down (i);
return min (query (i << 1, l, r), query (i << 1 | 1, l, r));}
int gf (int x, int y){
for (int i = 25; i >= 0; i--) if (y >= (1 << i)) x = f[x][i], y -= (1 << i);
return x;}
void rupdate (int x, int y, int z){
while (top[x] != top[y]){
if (dep[top[x]] < dep[top[y]]) swap (x, y);
update (1, id[top[x]], id[x], z);
x = f[top[x]][0];}
if (dep[x] > dep[y]) swap (x, y);
update (1, id[x], id[y], z);}
int rquery (int x){
if (x == root) return t[1].minn;
int y = gf (root, dep[root] - dep[x] - 1);
if (dep[x] >= dep[root] or f[y][0] != x) return query (1, id[x], id[x] + siz[x] - 1);
int aa, bb;
aa = query (1, 1, id[y] - 1), bb = query (1, id[y] + siz[y], n);
return min (aa, bb);}
signed main (){
scanf ("%lld %lld", &n, &m);
for (int i = 1; i < n; i++){
int u, v;
scanf ("%lld %lld", &u, &v);
add (u, v), add (v, u);}
for (int i = 1; i <= n; i++) scanf ("%lld", &val[i]);
dfs1 (1, 0), dfs2 (1, 1), build (1, 1, n);
scanf ("%lld", &root);
while (m --){
int op, x, y, z;
scanf ("%lld", &op);
if (op == 1) scanf ("%lld", &root);
if (op == 2) scanf ("%lld %lld %lld", &x, &y, &z), rupdate (x, y, z);
if (op == 3)scanf ("%lld", &x), printf ("%lld\n", rquery (x));}}