演算法 樹上倍增求LCA

2022-04-30 06:00:09 字數 1875 閱讀 1686

lca指的是最近公共祖先(least common ancestors),如下圖所示:

4和5的lca就是2

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

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

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

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

如果一下子跳到目標點記憶體又可能不支援,相對來說倍增的價效比算是很高的

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

於是可以寫出這段**

1

if(depth[a]

2int c = depth[a] -depth[b];

3for(int i = 0; i <= 14; i++)

7 }

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

所以倍增找lca的方法是這樣的:

從最大可以跳的步數開始跳(一定是2i),如果跳的到的位置一樣,就不跳,如果不一樣才跳,每次跳的路程是前一次的一半

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

把這一段寫成**,就成了這樣:

1

for(int i = 14; i >= 0; i--)

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 matrixup;

2 inline void

init_bz()

8for(int j = 1; j <= 14; j++)12}

13 }

注意倍增求lca適用於詢問多的情況,不然光在預處理上花的時間就已經夠多了(如果只有一兩個詢問,直接暴力就好了)

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

同樣,點$u$,$v$的lca還滿足它是其中乙個點的最近的乙個祖先,滿足$u$,$v$都在它的子樹中。

判斷乙個點是否在另乙個點的子樹中,我們可以用dfs序來判斷。

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

1 

void dfs(int p, int fa)

10 out[p] = cnt;

11 }

12 13

int lca(int a, int b)

22 return bz[a][0];

23 }

24

樹上倍增法求LCA

我們找的是任意兩個結點的最近公共祖先,那麼我們可以考慮這麼兩種種情況 1.兩結點的深度相同.2.兩結點深度不同.第一步都要轉化為情況1,這種可處理的情況。先不考慮其他,我們思考這麼乙個問題 對於兩個深度不同的結點,把深度更深的那個向其父節點迭代,直到這個迭代結點和另乙個結點深度相同,那麼這兩個深度相...

12 16 樹上倍增法求LCA

1.預處理 節點的深度d 到根節點的距離dist 該點向上走2 k步能夠到達的點 f陣列。2.lca 將兩個節點調整到同乙個深度,只調整深的那個即可。如果 結束後,這兩個點重合,說明該點就是所求的點。否則,從大往小開始試跳躍的步數,直至將這兩個點調整為目標點的兩個子節點。最後兩點的父節點就是所求的l...

樹上倍增求LCA(最近公共祖先)

前幾天做faebdc學長出的模擬題,第三題最後要倍增來優化,在學長的講解下,嘗試的學習和編了一下倍增求lca 我能說我其他方法也大會嗎?倍增求lca father i j 表示節點i往上跳2 j次後的節點 可以轉移為 father i j father father i j 1 j 1 此處注意迴圈...