專題學習 LCT

2021-08-28 18:43:47 字數 3167 閱讀 5909

等下發樹剖的學習筆記

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...