我当时知道ST表可以 $O(1)$ 求 LCA 的时候是极为震惊的,可以在需要反复使用 LCA 的时候卡常使用。

ST表!用于解决 RMQ问题

ST表

我可能写得不好,看专业的

怎么实现?

考虑把求 LCA 转换为 RMQ问题。我们对于树求一遍欧拉序,就是那个回溯也会记录的那个。我们处理出每个数第一次在欧拉序中出现的位置,欧拉序上每个位置的深度,以及欧拉序上每个位置出现的点的编号。这些信息都可以在一次 $dfs$ 中求出。然后不难发现在回溯过程中加入的点是之前遍历的点的祖先,由此也不难推出结论。

在两个点在欧拉序上第一次出现的位置的区间中间的深度最小的点就是这两个点的 LCA

那么直接变成 RMQ问题,上 ST表。

Code:

void dfs(int u,int f,int deep){
	dfn[u]=++idx;
	app[idx]=u;
	dep[idx]=deep;
	for(int i=head[u];~i;i=nxt[i]){
		if(to[i]==f) continue;
		dfs(to[i],u,deep+1);
		app[++idx]=u;
		dep[idx]=deep;
	}
}
inline void init_ST(){
	for(int i=1;i<=(n<<1);++i){
		st[i][0]=i;
	}
	lg2[1]=0;lg2[2]=1;
	for(int i=3;i<=(n<<1);++i){
		lg2[i]=lg2[i>>1]+1;
	}
	for(int j=1;(1<<j)<=(n<<1);++j){
		for(int i=1;i+(1<<j)-1<=(n<<1);++i){
			int a=st[i][j-1],b=st[i+(1<<(j-1))][j-1];
			if(dep[a]<dep[b]) st[i][j]=a;
			else st[i][j]=b;
		}
	}
}
inline int LCA(int a,int b){
	int l=dfn[a],r=dfn[b];
	if(l>r) Swap(l,r);
	int k=lg2[r-l+1];
	int x=st[l][k],y=st[r-(1<<k)+1][k];
	return dep[x]<dep[y]?app[x]:app[y];
}