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<< 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< <= dep[a]; i++); 6 i--; 7 //使a,b两点的深度相同 8 for(j = i; j >= 0; j--) 9 if(dep[a] - (1<= 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 }