LCA 有几种经典的求取方法、这里只给出模板,至于原理不详叙。
1、RMQ转LCA
大致就是 DFS求出欧拉序 => 对欧拉序做ST表 => LCA(u, v) 即为 u、v 最先出现在欧拉序中的编号之间的最小值。
因为 LCA 的子树中必定有一个节点是 u,一个是 v,而且必定在两个节点到根节点的唯一路径上。
例如有欧拉序列 1 2 1 3 4 3 1 则 LCA(2, 3) == 1 、首次出现 2 的下标是 2、首次出现 3 的下标是 4、则 LCA 就是下标 2~4 之间的最小值即 1
#include<bits/stdc++.h> using namespace std; const int maxn = 1e4 + 10;///顶点数 struct EDGE{ int v, nxt, w; }Edge[maxn<<1]; int Head[maxn], cnt; int n, q, s;///点数、问询数、dfs起点 int fp[maxn]; int dfsLen;///每个顶点在欧拉序中第一次出现的位置、DFS序的长度(用做时间戳) int id[maxn]; int idLen;///每个顶点在DFS序中访问次序(用于离散化)、id数组的长度 int dp[maxn<<1][21];///跑ST表的dp数组(第二维应开到 ceil(log2(maxn<<1)) ) inline void init() { memset(dp, 0, sizeof(dp)); memset(Head, -1, sizeof(Head)); cnt = 0; idLen = dfsLen = 0; } inline void AddEdge(int from, int to, int weight) { Edge[cnt].v = to; Edge[cnt].nxt = Head[from]; Head[from] = cnt++; } void dfs(int v, int Fa) { int tmp; id[tmp = ++idLen] = v; dp[fp[v] = ++dfsLen][0] = tmp; for(int i=Head[v]; i!=-1; i=Edge[i].nxt){ int Eiv = Edge[i].v; if(Eiv == Fa) continue; dfs(Eiv, v, d); dp[++dfsLen][0] = tmp; } } void GetST() { for(int j=1; (1<<j)<=dfsLen; j++){ for(int i=1; i+(1<<j)-1<=dfsLen; i++){ dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]); } } } int LCA(int u, int v) { if(fp[u] > fp[v]) swap(u, v); int L = fp[u], R = fp[v]; int k = (int)(log(R-L+1)/log(2)); return id[min(dp[L][k], dp[R-(1<<k)+1][k])]; } int main(void) { scanf("%d %d %d", &n, &s, &q); for(int i=1; i<n; i++){ int u, v; scanf("%d %d", &u, &v); AddEdge(u, v); AddEdge(v, u); } dfs(s, -1); GetST(); while(q--){ int u, v; scanf("%d %d", &u, &v); printf("%d\n", LCA(u, v)); } return 0; }
2、树上倍增
3、Tarjan算法( 离线 )