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 此處注意迴圈...