求 LCA

一、倍增

预处理 \(O(n\log n)\),单次查询 \(O(\log n)\)

二、tarjan

狗都不用。询问离线。总共 \(O(n\times a(n) + q)\)

三、树剖

预处理 \(O(n)\),查询 \(O(\log n)\)。比倍增常数小很多。

int Lca(int x, int y) {
	while(top[x] != top[y]) {
		if(dep[top[x]] < dep[top[y]]) swap(x, y);
		x = fa[top[x]];
	}
	return dep[x] < dep[y] ? x : y;
}

四、欧拉序列

注意!!!不是即这棵树的括号序列。

而是:递归 \(x\) 开始时,将 \(x\) 加入,每次递归完子树后,回溯时再将 \(x\) 加入。

每个点 1 次,每条边 1 次,所以欧拉序列的长度为 \(n+(n-1)=2n-1\)

定义【欧拉序】表示一个点在欧拉序列中第一次出现的位置 \(pos(x)\)

欧拉序有以下两条性质:

  • 任意两点简单路径上所有节点均在它们的欧拉序之间出现。

  • 任意两点欧拉序之间不会出现它们 LCA 子树外的点。

然后 st 表查询区间里面深度最小值对应的点即可。预处理线性对数,查询常数。

void dfs(int x, int y) {
	dep[x] = dep[y] + 1; fa[x] = y, dis[x] = dis[y] + (x <= n);
	f[dfn[x] = ++tcnt][0] = x;
	for(int to : E[x]) {
		if(to == y) continue ;
		dfs(to, x);
		f[++tcnt][0] = x; 
	}
}

int get(int x, int y) { return dep[x] < dep[y] ? x : y; }
int Lca(int l, int r) {
	if(l == r) return l;
	l = dfn[l], r = dfn[r];
	if(l > r) swap(l, r);
	int k = __lg(r - l + 1);
	return get(f[l][k], f[r - (1 << k) + 1][k]);
}
posted @ 2024-07-03 12:21  LCat90  阅读(6)  评论(0编辑  收藏  举报