LCA倍增法 求樹的最近公共祖先(模板)

2021-08-20 14:56:26 字數 4393 閱讀 6619

lca倍增法

求樹的最近公共祖先

查詢樹上任意兩個節點之間的路徑資訊,實際上就是找這兩個節點的lca,因為唯一路徑就是這兩個節點分別到lca會合,所以關鍵問題就是求lca,需要的路徑資訊可在找lca過程中維護。找lca的思想類似於rmq,定義fa[u][i]為節點u的第2^i個父節點,fa[u][0]就是u的父節點,fa[u][1]就為u的爺爺節點,那麼就有如下遞推式:

fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1];,可以理解為tmp到2^i個父親可以拆成先到2^(i-1)父親處,再從該父親處到相應的2^(i-1)父親處。

路徑上的權值總和也可以這樣得出:

gw[tmp][i] = gw[tmp][i - 1] + gw[fa[tmp][i - 1]][i - 1],可以理解為u到2^i個父親的路勁和可以拆成先到2^(i-1)父親處的路徑和,再從該父親處到相應的2^(i-1)父親處的路徑和。

先通過bfs預處理出每個節點u的fa[u][i],gw[u][i]和深度並記錄,通過不斷上公升節點來找到lca和路徑權值總和。

hihocoder 

#1062 : 最近公共祖先·一

題目鏈結

題意:給你n對父子關係,然後有m組查詢,每一組查詢包括兩個人名,輸出它們的最近公共祖先,沒有輸出-1.

ps:如果有乙個人都沒出現過或者兩個人都沒出現過並且不是同乙個人,則不知道它們的祖先關係,如果兩個人沒出現過但它們是同乙個人,則要輸出他自己。

思路:通過並查集將每乙個人的fa對映到自己的最終祖先上,建立乙個樹的根結點,將所有的目前的子樹的根結點(每個族譜的最終祖先)連線起來,然後,就是lca求每乙個結點的最近公共祖先了。如果是樹的根結點的話,意味著它們沒有最近公共祖先。

#include#include#include#include#includeusing namespace std;

const int maxn = 100010;

const int deg = 20; //樹的最多層數

struct edge edge[maxn * 2];

int head[maxn], tot; //前向星鍊錶,head為鏈頭,tot為邊的總數

int gw[maxn][deg]; //gw[u][i]表示結點u到2^i個父親結點的路徑總權值

int sum_path; //sum_path為全域性變數

void addedge(int u, int v, int w)

void init()

int fa[maxn][deg];//fa[i][j]表示結點i的第2^j個祖先

int deg[maxn];//深度陣列

void bfs(int root)

for (int i = head[tmp]; i != -1; i = edge[i].next) }}

int lca(int u, int v)

if (tu == tv)return tu; //如果此時在同一結點,說明已經到達最近公共祖先,返回最近最近公共祖先

for (int i = deg - 1; i >= 0; i--)

if (fa[tu][0] == fa[tv][0])

else //否則沒有最近公共祖先,返回-1

return -1;

}bool flag[maxn];

mapmaps;

string name[maxn];

char str1[maxn],str2[maxn];

string s1,s2;

int main() else

s2=str2;

if(maps[s2]==0) else

addedge(u,v,1);

addedge(v,u,1);

flag[v]=1;

} int root;

for(int i=1; ihihocoder  

題意:給你n對父子關係,其中第乙個人名為這個族譜的最終祖先,然後有m個查詢,每個查詢包括兩個人名,問它們的最近公共祖先,人名保證出現過,只有乙個連通的族譜。

思路:lca模板。

#include#include#include#include#includeusing namespace std;

const int maxn = 100010;

const int deg = 20; //樹的最多層數

struct edge edge[maxn * 2];

int head[maxn], tot; //前向星鍊錶,head為鏈頭,tot為邊的總數

int gw[maxn][deg]; //gw[u][i]表示結點u到2^i個父親結點的路徑總權值

int sum_path; //sum_path為全域性變數

void addedge(int u, int v, int w)

void init()

int fa[maxn][deg];//fa[i][j]表示結點i的第2^j個祖先

int deg[maxn];//深度陣列

void bfs(int root)

for (int i = head[tmp]; i != -1; i = edge[i].next) }}

int lca(int u, int v)

if (tu == tv)return tu; //如果此時在同一結點,說明已經到達最近公共祖先,返回最近最近公共祖先

for (int i = deg - 1; i >= 0; i--)

if (fa[tu][0] == fa[tv][0])

else //否則沒有最近公共祖先,返回-1

return -1;

}bool flag[maxn];

mapmaps;

string name[maxn];

char str1[maxn],str2[maxn];

string s1,s2;

int main() else

s2=str2;

if(maps[s2]==0) else

addedge(u,v,1);

addedge(v,u,1);

flag[v]=1;

} int root;

for(int i=1; i求樹的最近公共祖先和兩點之間路徑權值和的模板

poj1986

題目鏈結

題意:給你一棵樹,求兩點之間的權值和

思路:用離線的方法處理或者bfs的倍增也可以,普通dfs倍增思想做的lca超時了

#include#include#include#include#includeusing namespace std;

const int maxn = 100010;

const int deg = 20; //樹的最多層數

struct edge edge[maxn * 2];

int head[maxn], tot; //前向星鍊錶,head為鏈頭,tot為邊的總數

int gw[maxn][deg]; //gw[u][i]表示結點u到2^i個父親結點的路徑總權值

int sum_path; //sum_path為全域性變數

void addedge(int u, int v, int w)

void init()

int fa[maxn][deg];//fa[i][j]表示結點i的第2^j個祖先

int deg[maxn];//深度陣列

void bfs(int root)

for (int i = head[tmp]; i != -1; i = edge[i].next) }}

int lca(int u, int v)

if (tu == tv)return tu; //如果此時在同一結點,說明已經到達最近公共祖先,返回最近最近公共祖先

for (int i = deg - 1; i >= 0; i--)

if (fa[tu][0] == fa[tv][0])

else //否則沒有最近公共祖先,返回-1

return -1;

}bool flag[maxn];

char s[10];

int main()

bfs(1); //以結點1開始bfs遍歷建樹

scanf("%d", &q);

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

return 0;

}

倍增法求lca(最近公共祖先)

基本上每篇部落格都會有參考文章,一是彌補不足,二是這本身也是我學習過程中找到的覺得好的資料 大致上演算法的思路是這樣發展來的。想到求兩個結點的最小公共祖先,我們可以先把兩個的深度提到同一水平,在一步一步往上跳,直到兩個結點有了乙個公共祖先,依照演算法流程,這就是least common ancest...

樹,LCA,最近公共祖先,倍增

最近公共祖先,樹上倍增,lca,fa i j 表示 i 節點向上 2j 的祖先 很像dp,k 屬於 1 log n f x k f f x k 1 k 1 算lca時,先不妨設 d x d y 二進位制拆分 嘗試從x 向上走 k 2log n 21,20,檢查到的點是否比 y 深 每次檢查中,若是,...

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

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