LCA問題的三種求法

2021-08-24 18:01:28 字數 2781 閱讀 2070

先來看一道例題:

hdu 2586 how far away ?

題目描述的就是給你一棵n個節點的樹,然後q次詢問,每次詢問的內容是節點x和節點y的最近公共祖先(lca)

接下來就來說一說lca(最近公共祖先)

你需要準備的預備知識:

①st表處理rmq問題;

②並查集的思想以及實現;

③dfs遍歷整棵樹,維護一些值;

求lca的三種方法:

1、lca轉rmq;

做法:①首先按dfs序遍歷整棵樹,得到每個節點第一次出現的序號,每個節點的深度,以及每個節點所維護的值;

②對於每個詢問(a,b),判斷第一次出現a與第一次出現b的大小,化成乙個區間查詢最小值;

③st打表,o(1)回答每次區間最小值的查詢即可;

模板如下:

#include

#include

#include

#include

#include

using

namespace

std;

const

int maxn=1e5+10;

int dis[maxn],dep[maxn],first[maxn],pos[maxn];

int dp[maxn][25];

int cnt;

struct edge;

vector

g[maxn];

int min(int i,int j) //因為需要得到最小值的序號,因此更新的時候返回序號即可

int query(int l,int r) //用st表求rmq

int get_lca(int a,int b) //比較兩個節點誰先出現,確定rmq的範圍

void dfs(int u,int fa,int deep) //dfs遍歷每個節點

}int main()

); g[b].push_back(edge);

}dfs(1,-1,1);

init_rmq(2*n-1);

while(m--)

}return

0; }

2、tarjan演算法求lca;

做法:①對於當前節點x,遍歷所有子節點,當遍歷結束之後,將其整棵子樹合併到x,並保證合併之後的祖先為x;

②處理有關於x的詢問,對於詢問(x,y),如果y節點已經遍歷,則x與y的lca就是y節點所在的集合的祖先。否則,將其推遲到遍歷y的時候再處理。

其中合併和查詢集合祖先用到的是並查集

模板如下:

#include

#include

#include

#include

using

namespace

std;

const

int maxn=1e5+10;

int n,m,t,x,y,z,vis[maxn],dis[maxn],fa[maxn],ans[maxn];

struct node;

vector

a[maxn]; //儲存節點資訊

vector

b[maxn]; //儲存查詢資訊

void init() //初始化

}int find(int x) //查詢祖先節點

void union(int x,int y) //合併

void tarjan(int u)

} for(int i=0;i//搜尋關於節點u的查詢

}int main()

); a[y].push_back(node);

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

);b[y].push_back(node);

}tarjan(1);

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

printf("%d\n",ans[i]);

}return

0;}

3、倍增求lca;

做法:①預處理遍歷整棵樹,儲存每個節點的深度,並且遞推求出它的二進位制距離的祖先節點的維護值,這些值儲存在f[i][j]中,其中f[i][j]表示的是對於節點i,與它相距2^j次方距離的祖先節點的值。

②對於每個詢問(a,b),找到深度大的那個點(假設是a點),逆序遍歷j,其中j表示能夠往上跳的距離的log值,直到a和b兩個點的深度相等;

③接下來a和b兩個點一起往上跳,每次跳的高度和距離都必須相等,跳的方法同②,直到兩個點跳到同乙個點為止,那個點即為最近公共祖先;

模板如下:

#include

#include

#include

using

namespace

std;

const

int maxn=1e5+10;

int tot,head[maxn],dis[maxn],fa[maxn][25],dep[maxn];

struct nodeedge[maxn];

void add(int u,int v,int w) //無向邊,正反建兩次

void init(int n) //遞推預處理fa陣列

void dfs(int u,int pre,int deep,int d) //dfs遍歷整棵樹的所有節點

int query(int u,int v) //倍增查詢

int main()

}return

0;}

總結 LCA的4種求法

lca的求法有多重多樣,總結下來是下面這4種.希望大家可以加油 我們考慮dfs求出每乙個點的父親 在當前根下 然後直接先暴力跳到同乙個深度,再同時跳 void dfs int u,int f int lca int u,int v int lca int a,int b 考慮把乙個樹分成輕鏈與重鏈,...

逆序數的三種求法

未完待續。逆序數 乙個數列nums n 對任意兩個數,如果前面的數大於後面的數,那麼就稱它們為一對逆序數。我們用乙個陣列儲存逆序數的值,reverse i 表示nums i 之後且比nums i 小的數。下面我們用三種方法 第一種方法沒有實際意義 來求reverse陣列。1.完全暴力計數法 先將re...

最短路徑是三種求法

說是總結,其實自己也沒有學多長時間只是把自己這段時間的一些經驗總結下來,用來供後來的初學者漲點經驗吧。對於學習演算法,個人的理解就是首先要去理解演算法的本質,然後想想演算法的實現過程,如何用 去描述這個演算法,然後就是去記模板了 對於像我這種初學者來說,這一步其實蠻重要的 另外說下做最短路問題的一些...