等下發樹剖的學習筆記
lct(link—cut—tree) 是解決一類動態樹問題的資料結構
主要是給乙個有根樹的森林,然後有動態插入邊,刪除邊,詢問等操作
保證時刻是乙個森林
lct維護子樹資訊比較麻煩,這裡暫時不提
一、實邊和虛邊:
lct 會將兒子劃分為虛、實兩種兒子,
相應的邊稱為虛邊或實邊,且任意時刻乙個節點最多隻會有乙個實兒子(可能沒有)。由於樹的形態會改變,因此 lct 不是嚴格的劃分虛實兒子,而是動態地改變,它使用了資料結構來維護實鏈(連續的實邊),並且用的是靈活的 splay.
二、實路徑和path_parent:
實邊連成實路徑,且不可伸長。
乙個實邊的path_parent是指這個實邊的最淺節點的父親,用這個性質可以完成許多操作。
三、splay
維護實路徑可以用splay,因為實路徑上任意兩個點都是祖先與子孫的關係。換句話說,如果用深度作為關鍵字給結點排序,那麼我們將得到乙個唯一的有序結點序列。平衡樹中每個結點的左子樹中結點在實路徑中的深度都小於該點,右子樹中的都大於該結點,因此平衡樹的最左結點對應該路徑的頭部,最右結點對應該路徑的尾部。
四、基本操作:
※1.access(x)
這個是最重要的操作:以 x 為起點,一直到根節點,構造出一條鏈。
該操作將 x 到根結點的路徑上的所有邊都變為實邊, 當然,為了保持實邊、虛邊劃分的性質,一部分原來的實邊也要相應變為虛邊。注意該操作會將 x下方的實邊變為虛邊。該操作的步驟如下:
(1) 如果結點 x 不是其所在實路徑的尾部, 即 x有子結點與之用實邊相連, 那麼需要 「斷開」這條邊(斷開並不是將這條邊刪除,而只是將其轉變為虛邊)。方法是首先將結點 x用 splay操作旋轉到所在平衡樹的根結點,然後 x 肯定有右子樹,故將 x 與 x的右子樹分離,同時將 x 的右子樹的 path_parent 設定為 x。
(2) 如果結點 x所在的平衡樹包含根結點,那麼該過程結束;否則,轉步驟(3);
(3) 設 y 為 x 所在平衡樹的 path_parent。將 y 用 splay 操作旋轉到其所屬平衡樹的根結點,並且用 x 所在的平衡樹替換 y的右子樹,這樣就實現了實路徑的向上延伸。當然到這裡還沒有結束,我們需要分離原來 y 的右子樹,y原來的右孩子記為 p。此時 p 的 path_parent就為 y了,然後繼續轉步驟(2)。
實現時,我們可以把splay中根節點的fa改為path_parent,這樣每個點的父親,要麼是實路徑上的點,要麼是它所處實路徑的path_parent,特別地,這棵樹的根節點的 path_parent 為 0。這樣的話乙個點,它的 fa的左右兒子可能都不為它,因此判斷根節點的條件也需要稍微修改一下。
2.findroot(x):找到x所在的樹的根節點
我們access(x),然後x就和它的根節點在同一棵平衡樹里了,那麼根節點就是實路徑的頭部。
3.makeroot(x):使節點x成為其所在樹的根節點
其實就是把x到根的路徑取反。
先access(x),然後給splay的根節點打上翻轉標記,就可以將x到根的路徑取反了。
4.link(x,y)連線x,y這條邊
先makeroot(x),再將x的fa變成y就好了
5.cut(x,y)刪除一條邊
先把x變成根節點makeroot(x),這樣y就是x的子節點,然後就可以access(y),x和y就在同一棵平衡樹里了。然後對 y 執行 splay 操作使其
成為所在平衡樹的根結點,同時分離 y和 y 的左子樹,便完成了邊的刪除操作。
6.select(x,y):取出樹中(x,y)所在的路徑,進而執行路徑修改、路徑詢問等操作。
執行一次 makeroot(x)將 x 置為其所在樹的根,再執行一次 access(y)操作,就將 x 與 y以及它們路徑上的所有點整合至同一棵平衡樹中了。 此時我們在平衡樹的根節點上打上標記或者直接查詢資訊即可。
五.例題:
bzoj2049
傳送門**:
#include
#include
#include
#include
#include
using
namespace std;
const
int n =
1e4+3;
int n, m, u, v, qr, que[n]
;struct node t[n]
;inline
intwhich
(const
int&x)
inline
bool
isroot
(const
int&x)
inline
void
downdate
(const
int&x)
return;}
inline
void
rotate
(const
int&x)
inline
void
splay
(const
int&x)
rotate
(x);
}return;}
inline
void
access
(int x)
return;}
inline
intfindroot
(int x)
inline
void
makeroot
(const
int&x)
inline
void
link
(const
int&x,
const
int&y)
inline
void
cut(
const
int&x,
const
int&y)
char ch;
inline
intread()
intmain()
else
if(ch ==
'c')
link
(read()
,read()
);else
cut(
read()
,read()
);}return0;
}
動態規劃專題學習
一.明確dp陣列的含義 構造一維dp i 二維dp i j 明確其代表了什麼狀態,一般與題目的目的有關。如解決萬用字元問題,就定義boolean dp表示s i 1 與p j 1 的匹配成功與否 解決最長子陣列問題,就定義dp表示a i 1 與b j 1 的最長字尾公共陣列長度 解決最長子序列問題,...
微服務專題學習01
springboot結合前端有很多種方法,比如在static裡面直接加入css或js,又或者引入webjars,以jar包的形式加入專案 webjars形式 pom 檔案 引用bootstrap org.webjars groupid bootstrap artifactid 3.3.7 1 ver...
樹鏈剖分 專題,學習記錄
理解了上面的那個部落格中的想法之後,你可以看這個神犇寫的 spoj上的 第一版 調了一下午 半晚上,第一次a的時候那真是excited include include include include include include using namespace std typedef long l...