LCA的一些演算法

2022-04-02 23:25:55 字數 3045 閱讀 9574

lca,就是求樹上任意兩點的最近公共祖先

(本題與**均為luogu3379)

方法我好像講過乙個,這次把主要的三個一起講一講

<1> 倍增(o(n log n))

我們先考慮最基本的lca,記錄每乙個點的父節點和深度。

對於兩個點x,y,先將它們調到同一高度(令dep[x]>dep[y],即把x向上移(dep[x]-dep[y])步即可,然後一起往上走就可以了。

這複雜度是o(nq)的,所以在此基礎上優化。

用father[i][j]表示點j向上走2^i步時的點是多少(沒有就是-1),然後每次上移只要走log n次即可。

預處理的話 father[i][j]]=father[i-1][father[i-1][j]];遞推即可。

code

#include#include

#include

using

namespace

std;

const

int n=500005,p=25

;struct

data

e[n*2+10

];int head[n*2+10

],dep[n],father[p][n],i,j,n,m,x,y,root,k;

inline

void read(int &x)

inline

void write(int

x)inline

void add(int x,int

y)inline

void dfs(int k,int fa,int

d)inline

int lca(int x,int

y)

return father[0

][x];

}int

main()

dfs(root,-1,0

);

for (j=0;j1;++j)

for (i=1;i<=n;++i)

if (father[j][i]==-1) father[j+1][i]=-1; else father[j+1][i]=father[j][father[j][i]];

while (m--)

return0;

}

<2> dfs序+rmq(o(n log n))

不難發現,兩個點的lca就是它們第一次出現的位置之間深度最小的點(證明略)。

所以我們用rmq實現o(1)查詢,預處理是o(n log n)

注意rmq返回的是具體的點而不是深度

不得不提的是資料的first[x],first[y]值不一定是從小到大排的,需要判斷一下(re了很久)

code

#include#include

#include

#include

using

namespace

std;

const

int n=500005,p=25

;struct

data

e[n*2+10

];struct

rmqf[p][n*2+10

];int

head[n],first[n],n,m,root,i,j,k,tot,x,y;

inline

void read(int &x)

inline

void write(int

x)inline

void add(int x,int

y)inline

void dfs(int k,int fa,int

d)int

main()

dfs(root,-1,0

);

for (j=1;jj)

for (i=1;i+(1

<1

<=tot;++i)

if (f[j-1][i].x1][i+(1

<<(j-1))].x) f[j][i].x=f[j-1][i].x,f[j][i].num=f[j-1][i].num; else f[j][i].x=f[j-1][i+(1

<<(j-1))].x,f[j][i].num=f[j-1][i+(1

<<(j-1

))].num;

while (m--)

return0;

}

<3> tarjan(o(n+q))

這個我第一次寫這道題的時候已經寫過了,具體看

這裡給一下鄰接表的**(好不容易會打)

code

#include#include

using

namespace

std;

const

int n=500005

;struct

data

e[n*2+10

];struct

ques

q[n*2+10

];int

head[n],qhead[n],father[n],ans[n],i,k,qk,x,y,n,m,root;

bool

vis[n];

inline

void read(int &x)

inline

void write(int

x)inline

void add(int x,int

y)inline

void qadd(int x,int y,int

z)inline

int getfather(int

k)inline

void dfs(int

k)int

main()

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

for (i=1;i<=n;++i)

father[i]=i;

dfs(root);

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

write(ans[i]),putchar('\n

');return0;

}

一些重要的演算法

原文 http coolshell.cn p 2583 下面是一些比較重要的演算法,原文 羅 列了32個,但我覺得有很多是數論裡的或是比較生僻的,和計算機的不相干,所以沒有選取。下面的這些,有的我們經常在用,有的基本不用。有的很常見,有的 很偏。不過了解一下也是好事。也歡迎你留下你覺得有意義的演算法...

一些重要的演算法

酷殼 http coolshell.cn 原文 http coolshell.cn p 2583 下面是一些比較重要的演算法,原文羅列了32個,但我覺得有很多是數論裡的或是比較生僻的,和計算機的不相干,所以沒有選取。下面的這些,有的我們經常在用,有的基本不用。有的很常見,有的很偏。不過了解一下也是好...

一些重要的演算法

下面是一些比較重要的演算法,原文羅列了32個,但我覺得有很多是數論裡的,和計算機的不相干,所以沒有選取。下面的這些,有的我們經常在用,有的基本不用。有的很常見,有的很偏。不過了解一下也是好事。也歡迎你留下你覺得有意義的演算法。注 本篇文章並非翻譯,其中的演算法描述大部份摘自wikipedia,因為維...