求有根樹的任意兩個節點的最近公共祖先。
解答這個問題之前,咱們得先搞清楚到底什麼是最近公共祖先。最近公共祖先簡稱lca(lowest common ancestor),所謂lca,是當給定乙個有根樹t時,對於任意兩個結點u、v,找到乙個離根最遠的結點x,使得x同時是u和v的祖先,x 便是u、v的最近公共祖先。
———摘自度娘
可能度娘裡面的話有些讀不懂(我的智商原因),在這裡我先舉個栗子
在本圖中,我們記點n,m的最近公共祖先為lca(n,m),則:
lca(5,6)= 1;因為5的父親為2,2的父親為1,6的父親為3,而3的父親為1;1為首個出現的祖先,故lca(5,6)= 1。
另一種理解方式是把t理解為乙個無向無環圖,而lca(t,u,v)即u到v的最短路上深度最小的點。
這裡給出乙個lca的例子:
對於t=
v=e=
則有:lca(t,5,2)=1
lca(t,3,4)=3
lca(t,4,5)=3
———摘自度娘
這個演算法主要實現方式是用dp
查詢階段需要用到倍增
我們定義dp[i][j]表示i節點向上爬2
j2^j
2j後所到達的節點;查詢時,假設要查詢的兩個點n,m的最近公共祖先,還是以下圖為例查詢lca(7,6)。
7的深度為4,6的深度為3,首先把7這個結點的深度變為3,即7向上爬一層,變為4,然後就一起往上爬,第一次爬到2和3,2和3繼續爬就變成1,所以lca(7,6) = 1;
在查詢的時候,用倍增演算法可以把時間減少到log級別,**:
int
lca(
int x,
int y)
}return dp[x][0];}
初始化就不用說了吧……
void
init()
用dfs遍歷整顆樹,求出每個結點的父節點與子節點(因為輸入時是無序的)。時間複雜為o(n)。**:
void
find_son
(int now)}}
void
find_father
(int now)
}
#include
#include
using
namespace std;
const
int maxn =
1e5+5;
vector<
int> a[maxn]
, v[maxn]
;int dp[maxn][32
];int de[maxn]
, fa[maxn]
;bool vis[maxn]
;int n, m;
intlca
(int x,
int y)
}return dp[x][0
];}void
find_son
(int now)}}
void
find_father
(int now)
}void
dfs(
int now,
int step)
void
init()
void
read()
}void
write()
}int
main()
**可能有點複雜,將就著看吧……
(附:不了解倍增優化可以看一些rmq的部落格)
RMQ問題之ST演算法
st演算法 st演算法是用於解決rmq問題 區間最值問題 的一種強有力的工具。o nlogn 預處理,o 1 查詢最值,利用的是倍增的思想。但 但是,使用st演算法的條件是沒有修改操作,emmm 演算法流程 最大值為例 預處理 用f i j 表示,從i位置開始的2 j 個數中的最大值,例如f i j...
RMQ之ST演算法模板
1 include2 include 3 include4 using namespace std 5const int n 1e6 111 6 int max n 21 min n 21 a n 7void st int a,int n 預處理,o nlogn 820 21 22 23int lo...
RMQ問題之ST演算法
rmq問題 求長度為n的數列中,求 i,j 直接的最值。st演算法 一種動態規劃的方法。一 預處理dp陣列 對於區間 i,i 2 j 1 的最值,只需要知道區間 i,i 2 j 1 1 和區間 i 2 j 1 i 2 j 1 的最值即可。由此可的遞推方程 dp i,i 2 j 1 max dp i,...