LCA (最近公共祖先問題)

2021-08-04 23:54:47 字數 1799 閱讀 4282

摘自kiana810學長的講課內容

lca(least common ancestors),即最近公共祖先,是指在有根樹中,找出某兩個結點u和v最近的公共祖先(另一種說法,離樹根最遠的公共祖先)。

•眾所周知,兩點在樹上的路徑是唯一的,但是如何快速維護路徑的相關資訊(路徑權值和、路徑邊權最大/最小值等)?

•在解決這些問題之前,首先考慮乙個問題:如何快速找到兩點在樹上的路徑?

•假設兩個節點為x和y,若x是y的祖先或y是x的祖先,我們只需直接從深度較大的節點向上訪問即可找到路徑。

•若沒有這種祖先關係,則我們只要找到x和y的最近公共祖先,將兩個節點到他們最近公共祖先的路徑連線起來即可。

•與稀疏表類似(不知道稀疏表的自己查),我們不必儲存每個節點的所有祖先資訊,而是只儲存其所有2^k倍祖先(倍增),換句話說,用fa[x][k]表示x的2^k倍祖先。 •

fa[x][0]:x的1倍祖先,即x的父節點。 •

fa[x][1]:x的2倍祖先,即x父節點的父節點

• fa[x][2]:x的4倍祖先,即x父節點的父節點的父節點的父節點。

• ………………

• 更新操作也很簡單:fa[x][k+1]=fa[fa[x][k]][k]。

• 如何查詢乙個節點的k倍祖先?

• 方法:每次找到k的二進位制表示中,從低到高第乙個1所在的位置,假設是第x位,則先訪問該節點的第2x倍祖先,以此類推

• 可能會用到lowbit(不知道的自己查!)lowbit(x)=x&(-x);

• 如何查詢兩個節點的最近公共祖先?

• 直接做很困難,因為兩個節點的一部分祖先之間沒有聯絡,很難定位到他們的最近公共祖先,

• 想法:先將兩個節點的深度變到相同,使得它們同時向上再走相同的深度後可以到達最近公共祖先節點,而我們只需要確定後者的深度即可。

• 遵循以下步驟來找到兩個節點x和y的最近公共祖先:

• ①不妨設x和y的深度分別為dep[x]和dep[y],且dep[x]^k倍祖先不相等,然後將x和y都移動到

其2^k倍祖先的位置並更新他們的值,顯然k可以列舉也可以二分。

• ③假設不存在滿足條件的k,則說明此時x和y的父節點一定是同乙個點,而這個點就

是兩點的最近公共祖先。

1.模板

#include #include #include using namespace std;

inline int read()

while(ch >= '0' && ch <= '9')

return x * f;

}int n, m, root, cnt;

int depth[500010], head[500010], fa[500010][20];

bool vis[500010];

struct data

edge[500010 << 1];

inline void addedge(int u, int v)

void dfs(int x)

for(int i = head[x]; i; i = edge[i].next)

}inline int lca(int x, int y)

if(x == y) return x;

else return fa[x][0];

}int main()

dfs(root);

for(int i = 1, x, y; i <= m; ++i)

return 0;

}

2.bzoj 1787 [ahoi2008] meet 緊急集合(之前發過)

謝謝**~

最近公共祖先 LCA 最近公共祖先

直接暴力搜尋參考 普通搜尋每次查詢都需要 樸素演算法是一層一層往上找,倍增的話直接預處理出乙個 具體做法是 維護乙個 的關係來線性求出這個陣列 int anc n 31 int dep n 記錄節點深度 void dfs int u,int parent for int i 0 i g u size...

最近公共祖先 最近公共祖先(LCA)

如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。輸入格式 第一行包含三個正整數n m s,分別表示樹的結點個數 詢問的個數和樹根結點的序號。接下來n 1行每行包含兩個正整數x y,表示x結點和y結點之間有一條直接連線的邊 資料保證可以構成樹 接下來m行每行包含兩個正整數a b,表示詢問...

最近公共祖先問題(LCA)

最近公共祖先問題 lca 下面的內容來自演算法藝術與資訊學競賽一書 lca問題 給出乙個有根樹t,對於任意兩個節點u和v,求出 lca t,u,v 即離根最遠的結點x,使得x同時是u和v的祖先。從上面的遞推方法,給我們乙個啟示。當l u l v 時,可以根據lca u,v 的答案把所有結點分成若干個...