解决的问题:
把对树的操作转换到序列上,以便于用数据结构来维护。
基本概念:
- 树链:就是树上的路径。
- 剖分,就是把路径分类为重链和轻链。
- 重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
- 轻儿子:v的其它子节点。
- 重边:点v与其重儿子的连边。
- 轻边:点v与其轻儿子的连边。
- 重链:由重边连成的路径。
- 轻链:轻边。
需要处理的数组含义:
- siz[v]表示以v为根的子树的节点数。
- dep[v]表示v的深度(根深度为1)。
- fa[v]表示v的父亲。
- son[v]表示v的重儿子。
- top[v]表示v所在的重链的顶端节点。
- pos[v]表示v在线段树中的位置。
算法流程:
第一次dfs:
把fa,dep,siz,son求出来。
// in main() -> dep[1] = 1, dfs1(1);
void dfs1(int now){
int mx1 = 0, mx2 = 0;
size[now] = 1;
for(int i = head[now]; i; i = next[i]){
if(to[i] == fa[now]) continue;
fa[to[i]] = now, dep[to[i]] = dep[now] + 1;
dfs1(to[i]);
size[now] += size[to[i]];
if(mx1 < size[to[i]]) mx1 = size[to[i]], mx2 = to[i];
}
son[now] = mx2;
}
第二次dfs:
把top,pos求出来。
具体的,ž根节点为起点,向下拓展构建重链,优先选择重儿子进行dfs,完成之后在更新轻儿子。
// in main() -> top[1] = 1, dfs2(1);
void dfs2(int now){
pos[now] = ++ ct;
if(son[now] != 0) top[son[now]] = top[now], dfs2(son[now]);
for(int i = head[now]; i; i = next[i]){
if(to[i] == son[now] || to[i] == fa[now]) continue;
top[to[i]] = to[i], dfs2(to[i]);
}
}
建立数据结构(以线段树为例)
// in main() -> for(int i = 1; i <= n; i ++) v[pos[i]] = val[i]; -> 反射节点
// in main() -> build(1, 1, n);
void build(int now, int l, int r){
if(l == r){t_max[now] = v[l]; return;}
build(lch, l, mid);
build(rch, mid+1, r);
t_max[now] = std::max(t_max[lch], t_max[rch]);
}
查询:
int que_max(int x, int y){
int res = -inf;
while(top[x] != top[y]){ // 一直跳到同一条链上
if(dep[top[x]] < dep[top[y]]) std::swap(x, y); // 每次走深的一个
res = std::max(res, que_max(1, 1, n, pos[top[x]], pos[x]));
x = fa[top[x]]; // 到上一个链上去
}
if(pos[x] > pos[y]) std::swap(x, y); // 处理在同一条链上的情况
res = std::max(res, que_max(1, 1, n, pos[x], pos[y]));
return res;
}
时间复杂度:
树链剖分有一个很强的性质:从任意一个叶子节点出发,到根节点的路径上最多有
logn
条重链。
所以再计算上线段树区间查询的复杂度
logn
,总的时间复杂度为
log2n
。
SDOI必备模板(非递归版)
void pre(){
int l = 1, r = 0; tp = 0;
q[++ r] = 1, dep[1] = 1;
while(l <= r){
int x = q[l ++]; sz[x] = 1;
for(int i = head[x]; i; i = nxt[i]){
uint u = to[i]; if(u == fa[x]) continue;
dep[u] = dep[x] + 1, fa[u] = x, q[++ r] = u;
}
}
for(int i = n; i >= 1; i --){
int x = q[i]; if(!fa[x]) continue;
sz[fa[x]] += sz[x];
if(sz[son[fa[x]]] < sz[x]) son[fa[x]] = x;
}
st[++ tp] = 1, top[1] = 1;
while(tp){
int x = st[tp --]; pos[x] = ++ tim, num[tim] = x;
for(int i = head[x]; i; i = nxt[i]){
uint u = to[i]; if(u == fa[x] || u == son[x]) continue;
st[++ tp] = u, top[u] = u;
}
if(son[x]) st[++ tp] = son[x], top[son[x]] = top[x];
}
}