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 7using
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與父節點所在集合合併,之後對於它的所有...