想法就是把樹壓平,然後紀錄當前節點u的深度在dep[u],紀錄時間戳記,進入的時候要紀錄一次,出來的時候也要記錄,把當前點u進入的時間戳紀錄在in[u]中,時間戳的大小為2*n,所以必須用兩倍的記憶體來維護。之後就轉成RMQ問題
查詢a,b兩點的LCA=dep[in[a]]到dep[in[b]]中,最小深度的時間戳經過的那個點
查詢:
RMQ的部分就要自己維護了,使用線段樹效能會非常差,在n,q<=100000的情況下使用稀疏表的效能與使用樹鏈剖分的效能差不多
\(
\newcommand{\ord}[1]{\mathcal{O}\left(#1\right)}
\newcommand{\abs}[1]{\lvert #1 \rvert}
\newcommand{\floor}[1]{\lfloor #1 \rfloor}
\newcommand{\ceil}[1]{\lceil #1 \rceil}
\newcommand{\opord}{\operatorname{\mathcal{O}}}
\newcommand{\argmax}{\operatorname{arg\,max}}
\newcommand{\str}[1]{\texttt{"#1"}}
\)
2015年8月31日 星期一
2015年7月22日 星期三
[ Heavy-Light Decomposition ] 樹鏈剖分模板+LCA
在某些狀況下,我們希望對樹上a點到b點的路徑進行修改或查詢的動作,而且這些動作用一般的樹壓平RMQ或是離線處理無法完成的,這個時候我們可以利用特殊的方法將樹拆成長鏈。
長鏈可以搭配良好的資料結構。只要找出樹上所有長鏈,每條長鏈套用線段樹、BIT、Sparse Table、BST、Heap,就能降低時間複雜度。
可以利用簡單的做法將dfs序切成許多長鏈:
我們可以進行一個簡單的證明:
這些操作都可以在求LCA的過程中完成:
若樹鏈剖分的目的只是為了求LCA,build_link()及find_lca()有更好的做法:
這就只需進行一次DFS
長鏈可以搭配良好的資料結構。只要找出樹上所有長鏈,每條長鏈套用線段樹、BIT、Sparse Table、BST、Heap,就能降低時間複雜度。
可以利用簡單的做法將dfs序切成許多長鏈:
- 首先先設定其中一點為根,算出每個子樹有多少節點
- 樹上每個節點各自連向最大的子樹,相連的部分會自然形成鏈
我們可以進行一個簡單的證明:
- 假設節點數為V,由根往任意葉節點走,一旦遇到新鏈,新鏈的子樹必小於等於原鏈子樹,剩下的節點數量不到一半,所以沿途最多遇到 logV 條鏈。
這些操作都可以在求LCA的過程中完成:
若樹鏈剖分的目的只是為了求LCA,build_link()及find_lca()有更好的做法:
這就只需進行一次DFS
2015年5月28日 星期四
[ Persistent Binary Index Tree ] 持久化BIT
(二分索引樹)BIT是一種容易實現的資料結構,但在持久化的應用上是非常困難的,但我們可以犧牲些許的複雜度來簡化其實現方式
一般區間和的BIT長這樣:
這是持久化的版本(也可以用pair來實作):
可以知道其更新的複雜度為\(\ord{logN}\),查詢複雜度為\(\ord{logN*logQ}\),Q是查詢次數
一般區間和的BIT長這樣:
這是持久化的版本(也可以用pair來實作):
可以知道其更新的複雜度為\(\ord{logN}\),查詢複雜度為\(\ord{logN*logQ}\),Q是查詢次數
訂閱:
文章 (Atom)