树链剖分
树链剖分(树剖/链剖)有多种形式,如 重链剖分,长链剖分 和用于 Link/cut Tree 的剖分(有时被称作“实链剖分”),大多数情况下(没有特别说明时),“树链剖分”都指“重链剖分”。
引自:OI-WIKI
重链剖分
我们给出一些定义
定义 重子节点 表示其子节点中子树最大的子结点。如果有多个子树最大的子结点,取其一。如果没有子节点,就无重子节点。
定义 轻子节点 表示剩余的所有子结点。
从这个结点到重子节点的边为 重边。
到其他轻子节点的边为 轻边。
若干条首尾衔接的重边构成 重链。
简单来说,我们要做的就是把一颗树剖分成一条条链(如图左到图右)
具体点:我们把落单的结点也当作重链,那么我们要将整棵树就被剖分成若干条重链。
实现
分两个 DFS,所需变量:
fa[] : fa[i] 表示第 i 个节点的父亲节点;
Hson[] : Hson[i] 表示第 i 个节点的重儿子节点;
Dep[] : Dep[i] 表示第 i 个节点的深度(根节点深度为 1);
Size[] : Size[i] 表示第 i 个节点的子树大小;
Top[] : Top[i] 表示第 i 个节点所在的链的顶部节点;
seg[] : seg[i] 表示第 i 个节点的 DFS 序;
rev[] : 表示 DFS 序所对应的节点编号。
CODE:
void Podfs1(int p, int f) {
fa[p] = f;
Dep[p] = Dep[f] + 1;
Size[p] = 1;
for (int i = head[p]; i; i = node[i].next) {
int v = node[i].y;
if (v == f) continue;
Podfs1(v, p);
Size[p] += Size[v];
if (Size[v] > Size[Hson[p]]) Hson[p] = v;
}
}
void Podfs2(int p, int top) {
++DFNtot;
Top[p] = top;
seg[p] = DFNtot;
rev[DFNtot] = p;
if (Hson[p])
Podfs2(Hson[p], top);
for (int i = head[p]; i; i = node[i].next) {
int v = node[i].y;
if (v == fa[p]) continue;
if (Hson[p] != v) Podfs2(v, v);
}
}
配合线段树与实现 LCA:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int _ = 1e5 + 7;
int N, M, P, R, val[_];
int fa[_], Hson[_], Dep[_], Size[_], Top[_], seg[_], rev[_], DFNtot = 0;
int head[_], Tot = 0;
struct Node {
int y, next;
Node() {next = 0;}
} node[2 * _];
void add(int x, int y) {
++Tot;
node[Tot].y = y;
node[Tot].next = head[x];
head[x] = Tot;
}
/*--树剖dfs--*/
void Podfs1(int p, int f) {
fa[p] = f;
Dep[p] = Dep[f] + 1;
Size[p] = 1;
for (int i = head[p]; i; i = node[i].next) {
int v = node[i].y;
if (v == f) continue;
Podfs1(v, p);
Size[p] += Size[v];
if (Size[v] > Size[Hson[p]]) Hson[p] = v;
}
}
void Podfs2(int p, int top) {
++DFNtot;
Top[p] = top;
seg[p] = DFNtot;
rev[DFNtot] = p;
if (Hson[p])
Podfs2(Hson[p], top);
for (int i = head[p]; i; i = node[i].next) {
int v = node[i].y;
if (v == fa[p]) continue;
if (Hson[p] != v) Podfs2(v, v);
}
}
/*--线段树--*/
struct Tree {
int l, r, sum;
int lazy;
} tree[4 * _];
int lson(int root) {return root << 1;}
int rson(int root) {return root << 1 | 1;}
void Push_Up(int root) {
tree[root].sum = (tree[lson(root)].sum + tree[rson(root)].sum) % P;
}
void Push_Down(int root) {
if (tree[root].lazy != 0) {
int num = tree[root].lazy;
tree[lson(root)].sum = (tree[lson(root)].sum + (tree[lson(root)].r - tree[lson(root)].l + 1) * num % P) % P;
tree[rson(root)].sum = (tree[rson(root)].sum + (tree[rson(root)].r - tree[rson(root)].l + 1) * num % P) % P;
tree[lson(root)].lazy = (tree[lson(root)].lazy + num) % P;
tree[rson(root)].lazy = (tree[rson(root)].lazy + num) % P;
tree[root].lazy = 0;
}
}
void Build(int l, int r, int root) {
tree[root].l = l;
tree[root].r = r;
if (l == r) {
tree[root].sum = val[rev[l]] % P;
return ;
}
int mid = l + r >> 1;
Build(l, mid, lson(root));
Build(mid + 1, r, rson(root));
Push_Up(root);
}
void Change(int root, int l, int r, int L, int R, int num) {
if (L <= l && r <= R) {
tree[root].sum += (r - l + 1) * num % P;
tree[root].sum %= P;
tree[root].lazy += num % P;
tree[root].lazy %= P;
return ;
}
Push_Down(root);
int mid = l + r >> 1;
if (L <= mid)
Change(lson(root), l, mid, L, R, num);
if (mid < R)
Change(rson(root), mid + 1, r, L, R, num);
Push_Up(root);
return ;
}
int Query(int root, int l, int r, int L, int R) {
if (L > r || R < l) return 0;
if (L <= l && r <= R)
return tree[root].sum % P;
int ans = 0;
Push_Down(root);
int mid = l + r >> 1;
if (L <= mid) {
ans += Query(lson(root), l, mid, L, R) % P;
ans %= P;
}
if (mid < R) {
ans += Query(rson(root), mid + 1, r, L, R) % P;
ans %= P;
}
return ans % P;
}
/*--LCA--*/
void TChange(int u, int v, int num) {
while (Top[u] != Top[v]) {
if (Dep[Top[u]] > Dep[Top[v]]) {
Change(1, 1, DFNtot, seg[Top[u]], seg[u], num);
u = fa[Top[u]];
} else {
Change(1, 1, DFNtot, seg[Top[v]], seg[v], num);
v = fa[Top[v]];
}
}
if (Dep[u] > Dep[v]) swap(u, v);
Change(1, 1, DFNtot, seg[u], seg[v], num);
}
int TQuery(int u, int v) {
int ans = 0;
while (Top[u] != Top[v]) {
if (Dep[Top[u]] > Dep[Top[v]]) {
ans = ans + Query(1, 1, DFNtot, seg[Top[u]], seg[u]) % P;
ans %= P;
u = fa[Top[u]];
} else {
ans = ans + Query(1, 1, DFNtot, seg[Top[v]], seg[v]) % P;
ans %= P;
v = fa[Top[v]];
}
}
if (Dep[u] > Dep[v]) swap(u, v);
ans = (ans + Query(1, 1, DFNtot, seg[u], seg[v]) % P) % P;
return ans;
}
signed main() {
cin >> N >> M >> R >> P;
for (int i = 1; i <= N; i++) cin >> val[i];
for (int i = 1; i < N; i++) {
int x, y;
cin >> x >> y;
add(x, y);
add(y, x);
}
Podfs1(R, 0);
Podfs2(R, R);
Build(1, DFNtot, 1);
while (M--) {
int k;
cin >> k;
if (k == 1) {
int x, y, z;
cin >> x >> y >> z;
TChange(x, y, z);
} else if (k == 2) {
int x, y;
cin >> x >> y;
cout << TQuery(x, y) << '\n';
} else if (k == 3) {
int x, z;
cin >> x >> z;
Change(1, 1, DFNtot, seg[x], seg[x] + Size[x] - 1, z);
} else if (k == 4) {
int x;
cin >> x;
cout << Query(1, 1, DFNtot, seg[x], seg[x] + Size[x] - 1) << '\n';
}
}
return 0;
}