[算法]樹上倍增求LCA

  LCA指的是最近公共祖先(Least Common Ancestors),以下圖所示:算法

  4和5的LCA就是2數組

  那怎麼求呢?最粗暴的方法就是先dfs一次,處理出每一個點的深度spa

  而後把深度更深的那一個點(4)一個點地一個點地往上跳,直到到某個點(3)和另外那個點(5)的深度同樣code

而後兩個點一塊兒一個點地一個點地往上跳,直到到某個點(就是最近公共祖先)兩個點「變」成了一個點blog

  不過有沒有發現一個點地一個點地跳很浪費時間?內存

若是一會兒跳到目標點內存又可能不支持,相對來講倍增的性價比算是很高的it

  倍增的話就是一次跳2i 個點,不難發現深度差爲x時,深度更深的那個點就須要跳x個點ast

因而能夠寫出這段代碼class

1 if(depth[a] < depth[b])    swap(a, b);
2 int c = depth[a] - depth[b];
3 for(int i = 0; i <= 14; i++){
4     if(c & (1 << i)){
5         a = up[a][i];
6     }
7 }

  接下來很快就會發現一個很嚴重的問題:兩個點按照這樣跳,不能保證必定是最近的方法

因此倍增找lca的方法是這樣的:

  從最大能夠跳的步數開始跳(必定是2i),若是跳的到的位置同樣,就不跳,若是不同才跳,每次跳的路程是前一次的一半

  過程大概就像上圖所示,可是執行完了這一段到的點不是最近公共祖先,可是,它們再往上跳一格,就到了

把這一段寫成代碼,就成了這樣:

1 for(int i = 14; i >= 0; i--){
2     if(up[a][i] != up[b][i]){
3         a = up[a][i];
4         b = up[b][i];
5     }
6 }

  前面還須要加上一句特判(當a和b在同一邊時,深度淺的那個點就是最近公共祖先)

if(a == b)    return a;

  好了,會求lca了,關鍵是怎麼構造倍增數組。

沒有疑問的是向上跳一格就是本身的父節點

f[i][0] = fa[i];

  這個是初值,接着能夠根據這個推出來其餘的,除此以外還要附上初值0,否則有可能會RE

f[i][j] = f[f[i][j - 1]][j - 1];

  就是把這一段路,分紅兩段已經知道的

  完整代碼就是這樣的:

 1 Matrix<int> up;
 2 inline void init_bz(){
 3     up = Matrix<int>(16, n + 1);
 4     memset(up.p, 0, sizeof(int) * 16 * (n + 1));
 5     for(int i = 1; i <= n; i++){
 6         up[i][0] = fa[i];
 7     }
 8     for(int j = 1; j <= 14; j++){
 9         for(int i = 1; i <= n; i++){
10             up[i][j] = up[up[i][j - 1]][j - 1];
11         }
12     }
13 }

  注意倍增求LCA適用於詢問多的狀況,否則光在預處理上花的時間就已經夠多了(若是隻有一兩個詢問,直接暴力就行了)

 

  固然,這個倍增算法判斷條件是若干級祖先是否相等。

  一樣,點$u$,$v$的LCA還知足它是其中一個點的最近的一個祖先,知足$u$,$v$都在它的子樹中。

  判斷一個點是否在另外一個點的子樹中,咱們能夠用dfs序來判斷。

  這是倍增的另外一種判斷方法:

 1 void dfs(int p, int fa) {
 2     bz[p][0] = fa, in[p] = ++cnt;
 3     for (int i = 1; i < bzmax; i++)
 4         bz[p][i] = bz[bz[p][i - 1]][i - 1];
 5     for (int i = g.h[p]; ~i; i = g[i].nx) {
 6         int e = g[i].ed;
 7         if (e == fa)    continue;
 8         dfs(e, p);
 9     }
10     out[p] = cnt;
11 }
12 
13 int lca(int a, int b) {
14     if (dep[a] > dep[b])    swap(a, b);
15     if (in[a] <= in[b] && out[a] >= out[b])
16         return a;
17     for (int i = bzmax - 1, nx; ~i; i--) {
18         nx = bz[a][i];
19         if (!(in[nx] <= in[b] && out[nx] >= out[b]))
20             a = nx;
21     }
22     return bz[a][0];
23 }
24 
相關文章
相關標籤/搜索