dfs序
dfs序是对于一棵树而言,我们dfs的顺序。
主要目的是用于对一棵树上的结点形成一个数组,可以用于建线段树等操作。
在dfs序中,每个节点只出现一次(欧拉序每个节点出现两次)。
比如者一棵树的dfs序为 ABCDEF
时间戳
每个节点第一次被访问的时间
dfs序的性质
父节点的时间戳小于子节点的时间戳
树链剖分
可以解决的问题
对于树上两个节点
u
,
v
u, v
u,v
1、将路径
u
u
u 到
v
v
v 上的点都加上一个值
2、求路径
u
u
u 到
v
v
v 所有点的权值和
3、将节点
u
u
u 为根的子树都加上一个值
4、求节点
u
u
u 为根的子树所有点的权值和
重儿子
一个节点的子节点所在的子树中节点最多的子节点为重儿子
轻儿子
除重儿子以外的所有儿子
重链剖分
以重儿子优先的dfs序,将一棵树剖分为多个重链。每个重链以轻儿子开头,其余点都是重儿子。
对于树链剖分,我们需要记录以下几个值
d
f
n
[
x
]
dfn[x]
dfn[x] dfs序
f
a
[
x
]
fa[x]
fa[x] 父节点
s
z
[
x
]
sz[x]
sz[x] 节点大小
t
o
p
[
x
]
top[x]
top[x] 所在链的首节点
s
o
n
[
x
]
son[x]
son[x] 重儿子
d
e
p
[
x
]
dep[x]
dep[x] 深度
我们需要进行两次dfs,第一次求出 f a s z s o n d e p fa \ sz \ son\ dep fa sz son dep 的信息,第二次dfs求出 t o p d f n top \ dfn top dfn
求LCA
因为我们知道了每个节点的深度和所在重链的情况。所以我们可以这样考虑。
当两个点在同一条重链的时候,此时的
l
c
a
lca
lca 即为深度较浅的点。
当两个点不同一条重链的时候,此时让所在重链首节点较深的点往上跳,跳出它所在的这一条重链。
int LCA(int x, int y){
while(top[x] != top[y]){
if(dep[top[x]] > dep[top[y]]) swap(x, y);
y = top[y];
y = fa[y];
}
return dep[x] > dep[y] ? y : x;
}
性质
一个重链的dfs序是连续的
如何解决上述四个操作
操作1、2
用dfs序的时间戳建立线段树,然后我们在两个点的
L
C
A
LCA
LCA 的过程中更新线段树即可。
操作3、4
我们直接用dfs序和节点
x
x
x 的子树大小即可更新线段树,更新区间为
[
d
f
n
[
x
]
,
d
f
n
[
x
]
+
s
z
[
x
]
−
1
]
[dfn[x], dfn[x] + sz[x] - 1]
[dfn[x],dfn[x]+sz[x]−1]