## **題目描述**:
這是乙個裸的lca問題,即求書上兩個節點的最近公共祖先。
我們可以用樹上倍增來做;
當然,在做之前我們假設不知道該演算法。那麼我們如何來做這種型別的題目呢?
顯然,我們可以用暴力來做,找到兩點的最近公共祖先,我們可以用前向星存雙向邊,然後依次儲存每個點到的根的路徑。然後找到最先同時出現在兩條路徑的公共點即可;
顯然,這樣是可做的。但是這樣做,考慮到:
1.當書的深度非常大
2.當最壞情況樹退化為一條鏈式
3.當詢問次數非常多的時候。
以上,無論那種情況,起步都是o(n^2)的複雜度,顯然,這樣的做法並不夠優秀,所以我們引入倍增的做法,它可以nlogn的時間複雜度完成預處理,每次詢問logn的時間給出回答;
倍增演算法:所謂倍增,就是按2的倍數來增大,也就是跳 1,2,4,8,16,32 …… 不過在這我們不是按從小到大跳,而是從大向小跳,即按……32,16,8,4,2,1來跳,如果大的跳不過去,再把它調小。這是因為從小開始跳,可能會出現「悔棋」的現象。拿 5為例,從小向大跳,5≠1+2+4,所以我們還要回溯一步,然後才能得出5=1+4;而從大向小跳,直接可以得出5=4+1。這也可以拿二進位制為例,5(101),從高位向低位填很簡單,如果填了這位之後比原數大了,那我就不填,這個過程是很好操作的。
lca的倍增做法就是 用乙個fa[i][j]陣列 來儲存 i 節點的 2^j 倍父親是哪個節點; 這樣做每次都是2倍區間查詢,所以可以在logn的時間給出答案;
我們先跑一遍dfs, 初始化 深度 d陣列 , 初始化 fa[i][0] 。 跑一遍dfs後,d陣列儲存的就是每個節點的深度(預設根節點深度為1). fa陣列儲存的就是 fa[i][0] 就是 每個節點儲存的都是他父親節點的編號;
跑完dfs,我們需要對全域性fa進行賦值; 這裡用到的轉移方程是
fa[i][j] = fa[fa[i][j-1]][j-1] 。
比如(畫的比較醜)
2節點的 fa[2][0] 為 1 。 就是 2節點 的 (2^0 = 1)的父親為 1
4節點的fa[4][0]為 2。 就是 4號節點的父親為 2
要求 fa4 我們可以表示為 fa[fa[4][0]][0], 就是4號父親的父親, 這樣遞推上去,就可以的出每個fa;
完成這步之後,我們剩下就是求lca了;
先把x節點和y節點挪到同一層, 挪到同一層後, 若 x == y 代表一開始 x 就是y的祖先或者y是x的祖先;
若 x != y, 則 我們找到最深的滿足 fa[x][i] != fa[y][i] ;
然後返回x的父親節點即可。
至此,我們的演算法就結束了;
lca倍增學習:很明白的lca教程
1.建樹用前向星建, 注意邊數為n-1條,雙向邊,則應該開闢max_n * 2空間
2.這道題指明a是b的父親,得用並查集維護一下根節點,不然會wa的
#include
using namespace std;
#define max_n 10010
int head[max_n]
, cnt =
0, d[max_n]
, fa[max_n][30
];struct nodeedge[max_n *2]
;void
add(
int x,
int y)
void
dfs(
int k)}}
intlca
(int x,
int y)
for(
int i =
20; i>=
0; i--)}
if(x == y)
return x;
for(
int i =
20; i>=
0; i--)}
return fa[x][0
];}int pre[max_n]
;void
init
(int n)
intfind
(int x)
void
union
(int x,
int y)
}int
main()
for(
int i =
1; i
)int r =
find
(pre[1]
);fa[r][0
]=0, d[r]=1
;dfs
(r);
for(
int i =
1; i<=
20; i++)}
int t;
cin >> t;
while
(t--
)return0;
}
最近公共祖先 倍增演算法
模板題是這個樣子的 給你一顆有根樹,每次查詢兩個節點的最近公共祖先。最近公共祖先為何物?簡單來說,就是兩點間的路徑中深度最小的那個節點。那麼,有什麼辦法可以求最近公共祖先呢?最樸素的辦法 首先讓兩個節點中深度大的節點往上乙個乙個節點地跳,直到兩個節點深度一樣,然後一起跳,直到他們跳到同乙個點。顯然,...
LCA 最近公共祖先 (倍增演算法)
首先了解一下我們 最近公共祖先 e和g的lca為a l和j的lca為d k和f的lca為b 然後 倍增 用到了二進位制和 dp 的思想 倍增 就是 1 2 4 8 16 任何乙個數 都是可以右 這些數相加得到的。了解一下二進位制 首先 定義 fa i j 為 從 i 節點 向上走 2 j 個節點,d...
最近公共祖先 LCA 倍增演算法
樹上倍增求lca lca指的是最近公共祖先 least common ancestors 如下圖所示 4和5的lca就是2 那怎麼求呢?最粗暴的方法就是先dfs一次,處理出每個點的深度 然後把深度更深的那乙個點 4 乙個點地乙個點地往上跳,直到到某個點 3 和另外那個點 5 的深度一樣 然後兩個點一...