LCA演算法解析 Tarjan 倍增

2021-08-05 19:50:01 字數 3123 閱讀 2271

lca的tarjan演算法的時間複雜度為o(n+q)是一種離線演算法,要用到並查集。

tarjan演算法基於dfs,在dfs的過程中,對於每個節點位置的詢問做出相應的回答。

當然,暫時還沒那麼容易弄懂,所以建議結合下面的例子和標算來看看。

比如:8-1-(13,14),此時如果詢問(13,14)的話,則(13,14)的lca為1;如果沒有相應的詢問,則往上回溯,(13,14)的父親都是1了,再往上就是8了。再dfs 8-4-6-(15,7),一樣的,回溯時,15,7,4的lca就成4了。

#include #include 

#include

#include

#include

using

namespace

std;

const

int n=40000+5

;struct

edge

void add(int a,int b,int

c)}e,q;

int t,n,m,from,to,dist,in

[n],rt,dis[n],fa[n],ans[n];

bool

vis[n];

void dfs(int

rt)}

int getf(int

k)void lca(int

rt) vis[rt]=1

;

for (int i=q.fst[rt];i;i=q.nxt[i])

if (vis[q.y[i]]&&!ans[q.z[i]])

ans[q.z[i]]=dis[q.y[i]]+dis[rt]-2*dis[getf(q.y[i])];

}int

main()

return0;

}

對於這個演算法,我們從最暴力的演算法開始:

①如果a和b深度不同,先把深度調淺,使他變得和淺的那個一樣

②現在已經保證了a和b的深度一樣,所以我們只要把兩個一起一步一步往上移動,直到他們到達同乙個節點,也就是他們的最近公共祖先了。

#include #include 

#include

#include

#include

#include

using

namespace

std;

const

int n=10000+5

;vector

son[n];

int t,n,depth[n],fa[n],in

[n],a,b;

void dfs(int prev,int

rt)int lca(int a,int

b)int

main()

depth[

0]=-1

;

int rt=0

;

for (int i=1;i<=n&&rt==0;i++)

if (in[i]==0

) rt=i;

dfs(

0,rt);

scanf(

"%d%d

",&a,&b);

printf(

"%d\n

",lca(a,b));

}return0;

}

而實際上,一步一步往上移動太慢,我們可以做乙個預處理:

fa[i][j]表示節點i往上走2^j次所到達的祖先,那麼不難寫出轉移方程:

fa[i][0]=father[i],fa[i][j]=fa[fa[i][j-1]][j-1]

然後在求lca的時候,有這樣乙個性質:(假設a和b深度一樣)

設anst[x][y]為節點x網上走y步到達的祖先,對於乙個k,如果anst[a][k]==anst[b][k],那麼對於k'(k'>k),一定有anst[a][k']==anst[b][k'];對於乙個k,如果anst[a][k]!=anst[b][k],那麼對於k'(k'

於是求法就漸漸的現行了:

1. 把a和b移到同一深度(設depth[x]為節點x的深度),假設depth[a]<=depth[b],所以我們的目的是把b向上移動i=(depth[b]-depth[a])層,那麼,由於之前有預處理的fa陣列,我們把i寫成二進位制形勢,然後利用fa陣列來在log n的複雜度中完成;

2. 尋找a和b的lca下一層的兩個祖先。利用之前的那個性質,再利用倍增,如果a和b的第2^k個祖先不是同乙個,那麼把a改為fa[a][k],b改為fa[b][k],k減1;否則直接k減1;當然在這之前要實現確定k的最大值,從大往小處理下去。最終的結果就是fa[a][0]或者fa[b][0]。

注意點:如果a和b在調節深度之後已經是同乙個祖先的,那麼直接返回a或者b。

1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7

using

namespace

std;

8const

int n=10000+5

;9 vector son[n];

10int t,n,depth[n],fa[n][17],in

[n],a,b;

11void dfs(int prev,int

rt)19

int lca(int a,int

b)34

intmain()

46 depth[0]=-1;47

int rt=0;48

for (int i=1;i<=n&&rt==0;i++)

49if (in[i]==0

)50 rt=i;

51 dfs(0

,rt);

52 scanf("

%d%d

",&a,&b);

53 printf("

%d\n

",lca(a,b));54}

55return0;

56 }

LCA倍增演算法

一.倍增演算法的前期鋪墊 我們記節點v到根的深度為depth v 那麼如果節點w是節點u和節點v的最近公共祖先的話,讓u往上走 depth u depth w 步,讓v往上走 depth v depth w 步,都將走到節點w。因此,我們首先讓u和v中較深的乙個往上走 depth u depth v...

lca倍增演算法模板

時間限制 1 sec 記憶體限制 128 mb 提交 244 解決 36 提交 狀態 給一棵樹,節點數為n 1 n 250,000 和q 0 q 100,000 個詢問,對於每個詢問求出所求兩點的最近公共祖先 第一行 節點數n 以下n行,第i 1行 點i的父親節點father i 假定根的父親是0 ...

LCA的倍增演算法

lca,即樹上兩點之間的公共祖先,求這樣乙個公共祖先有很多種方法 每次將深度大的點往上移動,直至二者相遇 在o 2n 預處理重鏈之後,每次就將深度大的沿重鏈向上,直至二者在一條鏈上 先記錄所有的詢問,對樹進行一次dfs,對於搜尋到的點u,先將點u往下搜,再將點u與父節點所在集合合併,之後對於它的所有...