LCA之倍增簡單講解

2022-08-13 11:03:18 字數 3099 閱讀 7621

lca之倍增簡單講解

lca代指least common ancestor,翻譯過來就是最近公共祖先

如下圖,x和y的最近公共祖先就是二號節點

那麼,如何來求這個最近公共祖先呢?

1.暴力演算法

讓x和y一步一步向上爬,一直爬到相遇為止

x: 4->3->2

y: 6->5->2

可是這樣暴力實在是太慢了,我們要想辦法優化它

於是就有了倍增求解lca

2.倍增

倍增的意思就是按二的此方遞增,比如: 1->2->4->8->16......

在x與y向上爬找祖先時,我們不用每次向上爬乙個,而是向上爬2^n個,這樣程式的效率會高很多

怎麼倍增呢?我們需要從大到小跳,比如: 32->16->8->4...... 為什麼不是從小到大?應為可能會跳過,出現「悔棋」的情況

舉個例子:

我們要求x(5號節點)與y(10號節點)的 lca

倍增的時候,我們發現y的深度比x的深度要小,於是現將y跳到8號節點,使他們深度一樣:

這個時候,x和y的深度就相同了,於是我們按倍增的方法一起去找lca

我們知道n(10)之內最大的二的次方是8,於是我們向上跳8個單位,發現跳過了。

於是我們將8/2變成4,還是跳過了。

再將4/2變成2,跳到了0號節點。

雖然這時跳到了lca,但是如果直接if(x==y)就確定的話,程式無法判斷是剛好跳到了還是跳過了,應為跳過了x也等於y

於是我們需要剛好跳到lca的兒子上,然後判斷if(x的爸爸==y的爸爸)來確定lca

由於每乙個數都可以分解為幾個2^n的和(不信自己試),所以他們一定會跳到lca的兒子上

於是我們就找到了lca啦!

3.預處理

明白了基本思路之後,我們就開始寫程式了

可是在寫lca之前,我們還得進行預處理

要處理些什麼呢?

1.每乙個點的深度,以便到時候判斷

2.每乙個點2^i級的祖先,以便到時候倍增跳

於是我們用乙個dep陣列來記錄每乙個點的深度,再用乙個fa[i][j]表示節點i的2^j級祖先

dep陣列的更新就是他爸爸的深度+1,但是如何更新fa陣列呢?

fa[i][j]=fa[fa[i][j-1]][j-1]

這個式子是什麼意思呢?

fa[i][j]就是i的2^j級祖先,那fa[i][j-1]就是i的2^(j-1)級祖先。

又因為我們知道2^(j-1)+2^(j-1)=2^j,i的2^(j-1)級祖先的2^(j-1)級祖先 就是i的2^j級祖先。

這就是這句話的意思。**如下:

void dfs(int x,int

father) //x為當前節點,father為他的爸爸

for(int i=0;i) //列舉與他相鄰的邊(我用的是vector存圖)

} return

;}

4.lca主函式過程在上面已經講過了,直接上**:

int lca(int u,int

v) //u,v就相當於先講的x和y

}if(u==v) //如果u剛好等於v,即他們已經變成了同乙個點

for(int i=(int)(log(n)/log(2));i>=0;i--) //(int)(log(n)/log(2))就是n以內最大的2的次方,從最大的開始倍增

}return fa[u][0

]; //返回他們的爸爸,即是lca

}

5.完整**下面是模板題洛谷p3379的**:

#include #include 

#include

#include

#include

#include

using

namespace

std;

const

int maxn=500005

;vector

e[maxn];

int n,m,s,dep[maxn],fa[maxn][21

];int read() //

快讀

while

(isdigit(ch))

return ans*flag;

} void dfs(int x,int father) //

x為當前節點,father為他的爸爸

for(int i=0;i//

列舉與他相鄰的邊(我用的是vector存圖)

} return;}

int lca(int u,int v) //

u,v就相當於先講的x和y

}if(u==v) //

如果u剛好等於v,即他們已經變成了同乙個點

for(int i=(int)(log(n)/log(2));i>=0;i--) //

(int)(log(n)/log(2))就是n以內最大的2的次方,從最大的開始倍增

}return fa[u][0]; //

返回他們的爸爸,即是lca

}int

main()

dfs(s,

0); //

預處理

for(int i=1;i<=m;i++)

return0;

}

其他題目:

洛谷p3884 二叉樹問題

洛谷p2420 讓我們異或吧

洛谷p3398 倉鼠找sugar

LCA倍增講解

這道題值得一刷 題解裡的巨佬都用tarjan 窩太難了 實際上是他們太巨了 本人 馬蜂 碼風獨特,請見諒 include include include include using namespace std inline intread while ch 0 ch 9 return x f inl...

講解 模板 最近公共祖先(LCA)(倍增)

閱讀須知 我認為讀者已經掌握 或了解 了 倍增思想 樹 圖 的基本概念及簡單實現 存圖與建圖 dfs 嗯,我們來看看最近公共祖先 lca 的一種實現方式 倍增。話說什麼是最近公共祖先呢?emmm 大家如果知道樹的話,應該就知道父親節點與兒子節點了吧,那麼祖先就是父親的父親的父親的 總之在同一條樹鏈上...

倍增LCA複習

時間過去了如此之久,我連倍增lca都不怎麼記得了,要粗事啊。首先預處理層數和每個節點的父親,然後預處理p陣列,p i,j 表示i向上第2 j個祖先。最後對於每個詢問x,y先把x,y變成同一層數的 x或y向上走直到兩個層數相等 然後x,y同時向上走,直到x和y的父親相同位置。自 1.dfs預處理出所有...