本文已参与「新人创作礼」活动,一起开启掘金创作之路。
背景:有个题解需要介绍下这两者的性质,这里就顺便写了个总结了 核心:将树上问题转化成区间问题
欧拉序
欧拉序,有2*n或2*n-1个编号 dfs序,有n个编号
(2021年8月30日21点27分)upd:更新下面的第二种叫法为 欧拉环游序
欧拉序,我理解的有两种搞法(可能叫法有误,思想就是那个思想 )
- 进入节点记录,遍历完所有子节点后,出节点时,当前时间戳记录
- 进入节点记录,遍历子节点的时候,返回到本节点时记录
具体代码体现为 记录的是,节点的时间戳
记录的是,时间戳为 的是哪个节点(也就是“谁”的意思) 第一种
void dfs(int u) {
sa[++tim] = u;
dfn[u] = tim;
for (son...) dfs(son);
sa[++tim] = u;
}
第二种
void dfs(int u) {
sa[++tim] = u;
dfn[u] = tim;
for (son...) {
dfs(son);
sa[++tim] = u;
}
}
这两者之间在处理树上问题时有不同的性质 例如有一棵树为
1 2 1 3 2 4 2 5 3 6 3 7
这里时间戳,指的是第一次进入节点的时间 第一种欧拉序可以为,2*N
欧拉序:1 2 4 4 5 5 2 3 6 6 7 7 3 1 时间戳:1 2 3 3 5 5 2 8 9 9 11 11 8 1
第二种为,2*N-1
欧拉序:1 2 4 2 5 2 1 3 6 3 7 3 1 时间戳:1 2 3 2 5 2 1 8 9 8 11 8 1
第一种的性质:
- 树上任意一点 的子树,在第一次出现和最后一个 之间的所有出现的数,另外有个性质是这些出现次数和为偶数
举例:2 的子树,截取为
2 4 4 5 5 2
中的4 4 5 5
,自行尝试一下其它的子树 - 树上任意两点和,路径上的点,为最后一个到第一个 之间,出现奇数次的数。另外再加上lca(最近公共祖先)
举例:4 到 7 的树上路径
截取为
4 5 5 2 3 6 6 7
,出现奇数次的数字4 2 3 7
,加上lca,树上路径的节点最终为4 2 1 3 7
- 通常将树上的问题,转化为区间问题求解,比如树上莫队,求解树链上不同数字的个数等等
第二种的性质:
- 树上任意一点的子树为,第一次出现 和 最后一个之间出现了的所有数,除去本节点的数
举例:2的子树,截取为
2 4 2 5 2
,除去本节点为4 5
- 树上任意两点和,两点的 ,为第一次出现和第一次出现区间内,时间戳最小的那个值
举例:4到7的lca,截取为
4 2 5 2 1 3 6 3 7
,注意,这里的时间戳,指的是第一次进入节点的时间 按照截取的数字,给出时间戳为3 2 5 2 1 8 9 8 11
,时间戳最小的为1时刻,那么去找1时刻的那个点是谁,也就是欧拉序第1个位置的那个点,lca就是节点1 - 通常用作求 lca,结合ST表,在时间内求解 ,适合在频繁求解的场景
dfs序
仅进入节点的时候记录 代码体现为
void dfs(int u) {
sa[++tim] = u;
dfn[u] = tim;
for (son...) dfs(son);
}
d f s序:1 2 4 5 3 6 7 时间戳:1 2 3 4 5 6 7
性质:
- 树上任意点 的子树(包含)为,当前第一次出现的位置到往后 个节点的区间,表示 时计算的 以为根 的树大小,假设包含了
举例:
的子树,
那么截取为,
2 4 5
,如果要不包含,取往后,区间大小为即可,也就是4 5
- 树上两点 和 的 ,需要配合链剖分,将树链变成一块块连续的序列,跳求解,这里不细说,但是却是比较重要常用的一部分,详细可以找找树链剖分的板子题康康
- 通常结合树剖等等进行维护树上链上的信息,比如,给某条链全部节点加上1,求解最大最小值等等