LCA (最近公共祖先)倍增做法 —— O(nlogn)预处理 O(logn)(在线)查询

pa[a][j] 表示 a 结点的 2^j倍祖先(j = 0时 为直接父亲,j = 1时为父亲的父亲……)

1.首先预处理出所有结点的深度值dep和父亲结点

 1 void dfs(int u, int f, int d) {
 2     dep[u] = d;
 3     pa[u][0] = f;
 4     for(int i = 0; i < G2[u].size(); i++) {
 5         edge& e = E[G2[u][i]];
 6         int v = e.u == u ? e.v : e.u;
 7         if(v != f) {
 8             dfs(v, u, d+1);
 9         }
10     }
11 }

2.预处理出所有结点的 2^j 倍祖先

1 void pre() {
2     for(int j = 1; (1<<j) < n; j++) 
3         for(int i = 1; i <= n; i++) if(pa[i][j-1] != -1)
4             pa[i][j] = pa[pa[i][j-1]][j-1];    
5 }

3.查询操作,首先将 a,b中深度较大的结点上升到与深度较小的结点同一深度,然后两个结点同步上移,直到上移到最近公共祖先的直接儿子处。

 1 int lca(int a, int b)//最近公共祖先
 2 {
 3     int i, j;
 4     if(dep[a] < dep[b]) swap(a, b);
 5     for(i = 0; (1<<i) <= dep[a]; i++);
 6     i--;
 7     //使a,b两点的深度相同
 8     for(j = i; j >= 0; j--)
 9         if(dep[a] - (1<<j) >= dep[b])
10             a=pa[a][j];
11     if(a == b) return a;
12     //倍增法,每次向上进深度2^j,找到最近公共祖先的子结点
13     for(j = i; j >= 0; j--) {
14         if(pa[a][j] != -1 && pa[a][j] != pa[b][j]) {
15             a = pa[a][j];
16             b = pa[b][j];
17         }
18     }
19     return pa[a][0];
20 }
原文地址:https://www.cnblogs.com/Kiraa/p/6141217.html