Xor Tree - 一种小巧高效的存树方式

该方法局限性较强,只在常数(不用递归)与码量上优于普通存图法,可以当看乐子。

无法树剖,唐完了。

部分信息求解

我们定义 \(deg_{u}\) 表示节点 \(u\) 的度数,\(xr_{u}\) 表示 \(u\) 相邻的节点编号异或值。这里,我们令根节点的 \(deg\) 加一,以保证其根的地位。
我们从小到大 (\(1 \sim n\)) 遍历 \(u\)。当 \(deg_{u}=1\) 时,我们认为 \(xr_{u}\) 即为 \(u\) 的父亲节点,记作 \(f\)。这时,我们将 \(deg_{f}\leftarrow deg_{f}-1,deg_{u}\leftarrow 0,xr_{f}\leftarrow xr_{f}~\mathrm{xor}~u\)。若 \(deg_{f}=1\),则向上遍历。代码如下:

++deg[1];

for (int i = 1; i <= n; ++i) {
	for (int x = i, y; deg[x] == 1; x = y) {
		y = xr[x];
		--deg[y], deg[x] = 1, xr[y] ^= x;
	}
}

结束后的 \(xr\) 即为树中的父亲数组。容易发现,很多信息也可以在遍历的过程中求解,如子树大小 \(siz_{u}\),最深深度 \(mxdep_{u}\)。也存在无法遍历时求解的,如节点深度 \(dep_{u}\),这类信息需要父亲节点的信息,可以倒着节点遍历顺序处理。

dfs 序的求解

我们定义 \(dfn_{u}\) 表示 \(u\) 节点的 dfs 序,\(dif_{u}\) 表示 \(u\) 节点与父节点 dfs 序的差值,即 \(dfn_{fa_{u}}-dfn_{u}\)
这里还需要维护一个信息:\(u\) 节点子树大小 \(siz_{u}\)
在上述删除 \(u\) 节点的过程中,我们做以下操作:

  1. \(siz_{u}\leftarrow siz_{u}+1\)
  2. \(dif_{u}\leftarrow siz_{xr_{u}}+1\)
  3. \(siz_{xr_{u}}\leftarrow siz_{xr_{u}}+siz_{u}\)
    对于节点 \(u\),其所有祖先 \(dif\) 的和为其 \(dfn\),按上述遍历顺序反向遍历即可。

对边信息的解读

有些题目需要获得连向父亲的边,可以类似 \(xr\) 记录边编号的异或值,相似处理即可。

效率比较

【模板】最近公共祖先(LCA)为例,图表如下:

实现方法 耗时 内存 链接
邻接表 2.65s 121.33MB here
链式前向星 1.82s 102.60MB here
Xor Tree 1.79s 86.48MB here

(这道题体现不出它的优势。。。)

参考链接

posted @ 2025-01-24 11:52  cqbzljh  阅读(83)  评论(1)    收藏  举报