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;
}