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\) 节点的过程中,我们做以下操作:
- \(siz_{u}\leftarrow siz_{u}+1\)
- \(dif_{u}\leftarrow siz_{xr_{u}}+1\)
- \(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 |
(这道题体现不出它的优势。。。)

浙公网安备 33010602011771号