樹上倍增求lca
lca指的是最近公共祖先(least common ancestors),如下圖所示:
4和5的lca就是2
那怎麼求呢?最粗暴的方法就是先dfs一次,處理出每個點的深度
然後把深度更深的那乙個點(4)乙個點地乙個點地往上跳,直到到某個點(3)和另外那個點(5)的深度一樣
然後兩個點一起乙個點地乙個點地往上跳,直到到某個點(就是最近公共祖先)兩個點「變」成了乙個點
不過有沒有發現乙個點地乙個點地跳很浪費時間?
如果一下子跳到目標點記憶體又可能不支援,相對來說倍增的價效比算是很高的
倍增的話就是一次跳2i 個點,不難發現深度差為x時,深度更深的那個點就需要跳x個點,
於是可以寫出這段**
if(depth[a] < depth[b]) swap(a, b);接下來很快就會發現乙個很嚴重的問題:兩個點按照這樣跳,不能保證一定是最近的。所以倍增找lca的方法是這樣的:從最大可以跳的步數開始跳(一定是2i),如果跳的到的位置一樣,就不跳,如果不一樣才跳,每次跳的路程是前一次的一半int c = depth[a] - depth[b];
for(int i = 0; i <= 14; i++)
}
過程大概就像上圖所示,但是執行完了這一段到的點不是最近公共祖先,但是,它們再往上跳一格,就到了
把這一段寫成**,就成了這樣:
for(int i = 14; i >= 0; i--)前面還需要加上一句特判(當a和b在同一邊時,深度淺的那個點就是最近公共祖先) if(a == b) return a;}
好了,會求lca了,關鍵是怎麼構造倍增陣列。沒有疑問的是向上跳一格就是自己的父節點
f[i][0] = fa[i];
這個是初值,接著可以根據這個推出來其他的,除此之外還要附上初值0,不然有可能會re
f[i][j] = f[f[i][j - 1]][j - 1];
就是把這一段路,分成兩段已經知道的
完整**就是這樣的:
matrixup;注意倍增求lca適用於詢問多的情況,不然光在預處理上花的時間就已經夠多了。inline void init_bz()
for(int j = 1; j <= 14; j++)}}
1. dfs預處理出所有節點的深度和父節點
版本1
void dfs(int u)版本2}
inline void dfs(int2. 初始各個點的2^j祖先是誰 ,其中 2^j (j =0...log(該點深度))倍祖先,1倍祖先就是父親,2倍祖先是父親的父親......。i的2^j祖先就是i的(2^(j-1))祖先的2^(j-1)祖先:u) }
}
void init()3.從深度大的節點上公升至深度小的節點同層,如果此時兩節點相同直接返回此節點,即lca。否則,利用倍增法找到最小深度的 p[a][j]!=p[b][j],此時他們的父親p[a][0]即lca。
版本1:
int lca(int a,int b)版本2}a=p[a][0]; //這時a的father就是lca
}return a;
}
int lca(int a,int b)//最近公共祖先lca指的是最近公共祖先(least common ancestors),如下圖所示:} return p[a][0];
}
4和5的lca就是2
那怎麼求呢?最粗暴的方法就是先dfs一次,處理出每個點的深度
然後把深度更深的那乙個點(4)乙個點地乙個點地往上跳,直到到某個點(3)和另外那個點(5)的深度一樣
然後兩個點一起乙個點地乙個點地往上跳,直到到某個點(就是最近公共祖先)兩個點「變」成了乙個點
不過有沒有發現乙個點地乙個點地跳很浪費時間?
如果一下子跳到目標點記憶體又可能不支援,相對來說倍增的價效比算是很高的
倍增的話就是一次跳2i 個點,不難發現深度差為x時,深度更深的那個點就需要跳x個點,
於是可以寫出這段**
if(depth[a] < depth[b]) swap(a, b);接下來很快就會發現乙個很嚴重的問題:兩個點按照這樣跳,不能保證一定是最近的。所以倍增找lca的方法是這樣的:從最大可以跳的步數開始跳(一定是2i),如果跳的到的位置一樣,就不跳,如果不一樣才跳,每次跳的路程是前一次的一半int c = depth[a] - depth[b];
for(int i = 0; i <= 14; i++)
}
過程大概就像上圖所示,但是執行完了這一段到的點不是最近公共祖先,但是,它們再往上跳一格,就到了
把這一段寫成**,就成了這樣:
for(int i = 14; i >= 0; i--)前面還需要加上一句特判(當a和b在同一邊時,深度淺的那個點就是最近公共祖先) if(a == b) return a;}
好了,會求lca了,關鍵是怎麼構造倍增陣列。沒有疑問的是向上跳一格就是自己的父節點
f[i][0] = fa[i];
這個是初值,接著可以根據這個推出來其他的,除此之外還要附上初值0,不然有可能會re
f[i][j] = f[f[i][j - 1]][j - 1];
就是把這一段路,分成兩段已經知道的
完整**就是這樣的:
matrixup;注意倍增求lca適用於詢問多的情況,不然光在預處理上花的時間就已經夠多了。inline void init_bz()
for(int j = 1; j <= 14; j++)}}
1. dfs預處理出所有節點的深度和父節點
版本1
void dfs(int u)版本2}
inline void dfs(int2. 初始各個點的2^j祖先是誰 ,其中 2^j (j =0...log(該點深度))倍祖先,1倍祖先就是父親,2倍祖先是父親的父親......。i的2^j祖先就是i的(2^(j-1))祖先的2^(j-1)祖先:u) }
}
void init()3.從深度大的節點上公升至深度小的節點同層,如果此時兩節點相同直接返回此節點,即lca。否則,利用倍增法找到最小深度的 p[a][j]!=p[b][j],此時他們的父親p[a][0]即lca。
版本1:
int lca(int a,int b)版本2}a=p[a][0]; //這時a的father就是lca
}return a;
}
int lca(int a,int b)//最近公共祖先} return p[a][0];
}
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 最近公共祖先)之倍增演算法
概述 對於有根樹t的兩個結點u v,最近公共祖先lca t,u,v 表示乙個結點x,滿足x是u v的祖先且x的深度盡可能大。如圖,3和5的最近公共祖先是1,5和2的最近公共祖先是4 在本篇中我們先介紹一下倍增演算法 我們需要乙個陣列de i 來表示每乙個節點i的深度,用另一陣列parent i j ...
樹,LCA,最近公共祖先,倍增
最近公共祖先,樹上倍增,lca,fa i j 表示 i 節點向上 2j 的祖先 很像dp,k 屬於 1 log n f x k f f x k 1 k 1 算lca時,先不妨設 d x d y 二進位制拆分 嘗試從x 向上走 k 2log n 21,20,檢查到的點是否比 y 深 每次檢查中,若是,...