ST表求LCA整理

我们知道有很多种求LCA的方法,比如直接暴力向上跳求取LCA,倍增求LCA,还有tarjan求LCA(我不会),以及ST表求LCA

ST表求LCA的最大优点是预处理过后每次询问的复杂度是 \(O(1)\) 的,这非常的优秀。

在求LCA的过程中,ST表起到的还是一个查询区间最小值的操作,并不是什么奇奇怪怪的运用,请放心食用。

主要思路是我们给在dfs中经过的所有点都给予一个dfs序,当然,非叶子节点会被给予多个dfs序。我们按照dfs序将节点排序,得到一个数列,非叶子节点会多次出现。

我们将点的编号换成他最小的dfs序,又得到了一个序列。我们设 \(pre[u]\)\(u\) 的最小dfs序,\(pos[s]\) 为dfs序为s的节点编号,我们求出来c为dfs序区间 \(pre[u]\)\(pre[v]\) 之间的最小值,那么 \(pos[c]\) 即为他们的LCA。

洛谷P3376:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int read()
{
	int a = 0,x = 1;
	char ch = getchar();
	while(ch > '9' || ch < '0'){
		if(ch == '-') x = -1;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9'){
		a = a*10 + ch-'0';
		ch = getchar();
	}
	return a*x;
}
const int N=1e6+7;
int n,m,s;

int head[N],go[N],nxt[N],cnt;
void add(int u,int v)
{
	go[++cnt] = v;
	nxt[cnt] = head[u];
	head[u] = cnt;
}
int arr[N],pre[N],pos[N],fa[N][31],dis[N];
void dfs(int u)
{
	++cnt;
	arr[cnt] = cnt;
	pre[u] = cnt,pos[cnt] = u;
	for(int e = head[u];e;e = nxt[e]){
		int v = go[e];
		if(v == fa[u][0]) continue;
		dis[v] = dis[u]+1;
		fa[v][0] = u;
		dfs(v);
		arr[++cnt] = pre[u];
	}
}

int st[N][31],lg[N];

int js(int a,int b)
{
	if(a>b) swap(a,b);
	return min(st[a][lg[b-a+1]],st[b-(1<<lg[b-a+1])+1][lg[b-a+1]]);
}

int main()
{
	n = read(),m = read(),s = read();
	for(int i = 1;i < n;i ++){
		int u = read(),v = read();
		add(u,v);add(v,u);
	}
	cnt = 0;
	dfs(s);
	lg[1] = 0,lg[2] = 1;
	for(int i = 3;i <= cnt;i ++) lg[i] = lg[i/2]+1;
	for(int i = 1;i <= cnt;i ++) st[i][0] = arr[i];
	for(int j = 1;j <= 30;j ++){
		for(int i = 1;i +(1<<j)-1 <= cnt;i ++){
			st[i][j] = min(st[i][j-1],st[i+(1<<j>>1)][j-1]);
		}
	}
	for(int i = 1;i <= m;i ++){
		int u = read(),v = read();
		printf("%d\n",pos[js(pre[u],pre[v])]);
	}
	return 0;
}
posted @ 2020-09-13 23:32  nao-nao  阅读(766)  评论(0编辑  收藏  举报