dfs序和欧拉序构造方法及常用性质

520 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

背景:有个题解需要介绍下这两者的性质,这里就顺便写了个总结了 核心:将树上问题转化成区间问题

欧拉序

欧拉序,有2*n2*n-1个编号 dfs序,有n个编号

(2021年8月30日21点27分)upd:更新下面的第二种叫法为 欧拉环游序 欧拉序,我理解的有两种搞法(可能叫法有误,思想就是那个思想

  • 进入节点记录,遍历完所有子节点后,出节点时,当前时间戳记录
  • 进入节点记录,遍历子节点的时候,返回到本节点时记录

具体代码体现为 dfn[x]dfn[x] 记录的是,xx节点的时间戳

sa[y]sa[y]记录的是,时间戳为 yy 的是哪个节点(也就是“谁”的意思) 第一种

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 在这里插入图片描述

这里时间戳dfndfn,指的是第一次进入节点的时间 第一种欧拉序可以为,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

第一种的性质:

  • 树上任意一点 xx 的子树,在第一次出现xx最后一个 xx之间的所有出现的数,另外有个性质是这些出现次数和为偶数 举例:2 的子树,截取为2 4 4 5 5 2中的4 4 5 5,自行尝试一下其它的子树
  • 树上任意两点xxyy,路径上的点,为最后一个xx第一个 yy之间,出现奇数次的数。另外再加上lca(最近公共祖先) 举例:47 的树上路径 截取为4 5 5 2 3 6 6 7,出现奇数次的数字4 2 3 7,加上lca,树上路径的节点最终为4 2 1 3 7
  • 通常将树上的问题,转化为区间问题求解,比如树上莫队,求解树链上不同数字的个数等等

第二种的性质:

  • 树上任意一点的子树为,第一次出现 xx最后一个xx之间出现了的所有数,除去本节点的数 举例:2的子树,截取为2 4 2 5 2,除去本节点为4 5
  • 树上任意两点xxyy,两点的 lcalca,为第一次出现xx第一次出现yy区间内,时间戳最小的那个值 举例:47lca,截取为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表,在O(1)O(1)时间内求解 lcalca,适合在频繁求解lcalca的场景

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

性质:

  • 树上任意点 xx 的子树(包含xx)为,当前第一次出现xx的位置到往后 sz[x]sz[x] 个节点的区间,sz[x]sz[x]表示 dfsdfs 时计算的 以xx为根 的树大小,假设sz[x]sz[x]包含了xx 举例: 22 的子树,sz[2]=3sz[2]=3 那么截取为,2 4 5,如果要不包含xx,取xx往后,区间大小为sz[x]1sz[x]-1即可,也就是4 5
  • 树上两点 xxyylcalca,需要配合链剖分,将树链变成一块块连续的序列,跳toptop求解,这里不细说,但是却是比较重要常用的一部分,详细可以找找树链剖分的板子题康康
  • 通常结合树剖等等进行维护树上链上的信息,比如,给某条链全部节点加上1,求解最大最小值等等