您的位置:首页 > 其它

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

2016-12-07 15:02 302 查看

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 }

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: