lct的功能【題意】
乙個圖,有n個點,一開始圖中沒有邊。
三種操作:
connect u v:在點u和點v之間建一條邊。保證所有connect操作不會重複建邊。
destroy u v:摧毀點u到點v之間的邊。保證所有destroy操作將摧毀的是一條存在的邊。
query u v:詢問點u和點v是否聯通,是輸出yes,否則輸出no
保證:在任何時刻,任何點到任何點最多只有一條路徑。(就是叫你用樹咯)
【輸入格式】
第一行為兩個正整數n和m,分別表示點的個數和操作的個數。
以下m行,一行表示乙個操作。
每行開頭是乙個表示指令種類的字串s(區分大小寫),之後有兩個整數u和v (1≤u, v≤n且u≠v) 分別表示兩個點的編號。
【輸出格式】
對每個query操作,輸出點u和點v是否連通:是輸出yes,否則輸出no。
【樣例輸入】
200 5
query 123 127
connect 123 127
query 123 127
destroy 127 123
query 123 127
【樣例輸出】
noyes
no【樣例輸入】
3 5connect 1 2
connect 3 1
query 2 3
destroy 1 3
query 2 3
【樣例輸出】
yesno
【資料說明 】
10%的資料滿足n≤1000, m≤20000
20%的資料滿足n≤2000, m≤40000
30%的資料滿足n≤3000, m≤60000
40%的資料滿足n≤4000, m≤80000
50%的資料滿足n≤5000, m≤100000
60%的資料滿足n≤6000, m≤120000
70%的資料滿足n≤7000, m≤140000
80%的資料滿足n≤8000, m≤160000
90%的資料滿足n≤9000, m≤180000
100%的資料滿足n≤10000, m≤200000
【**】sdoi2008
lct的結構
如果這一題沒有前兩個操作,我們就可以用樹鏈剖分來解決這一系列的操作
但是,建邊和刪邊兩個操作改變了樹的結構。因此,我們就要用新的資料結構lct來做。
lct因為是動態的,所以建立在了資料結構splay上面,如果沒有學過splay,就趕緊去學吧。
言歸正傳,lct中那棵splay是由樹中的每乙個節點的深度作為權值進行操作的。
這樣有什麼好處呢?
父親節點和兒子節點有邊相連(不然就沒有這種關係),而且深度正好相差1,而splay中的節點都有這樣乙個性質——左邊的都比我小,右邊的都比我大,所以這樣構建splay還可以找到樹中節點和節點的關係了。
看到這裡,你可能會問,如果這棵樹不是二叉樹,而是森林的話,要怎麼處理?
這裡又要用到了splay的性質——將最新訪問過的點轉到根節點。
看乙個圖:
我們會發現這顆樹中有粗邊和細邊
和樹鏈剖分一樣,我們把由粗邊連起來的點用一顆splay維護
但是,因為樹中的點都是相通的,所以在splay中,有乙個新的機制:(很重要)
假如兩個點是由粗邊相連的,比如說1和2 ,那麼
tr[2].f = 1 並且 tr[1].son[0] = 2
假如兩個點是由細邊相連的,比如說2和3,那麼
tr[3].f = 2 但是 tr[2].son[0] = 0 ,而不是=2
還有,根據題目,lct實際上是一顆無根樹,並沒有固定的根,而是通過splay的操作將最新訪問過的點變成這顆無根樹第1層的點(只有乙個,可以說是根)
lct**的實現
lct的實現建立在乙個函式access(x)上面,這個函式的功能就是將x到x所在樹(注意:指的不只是所在的splay)的根之間的路徑全部變為粗邊,順便維護一下之前的節點(會改變與這些節點細邊和粗邊)
access函式:
inline
void access (
int x )
}//access中令人發懵的splay
int tmp[n]
;inline
void splay (
int x ,
int rt )
tmp[
++s]
= i ;
while
( s )
//這個操作是將整顆splay存放在tmp中並維護splay中的翻轉標記
//為什麼會由翻轉,等一下即可見分曉
while
( tr[x]
.f!=rt &&
(tr[tr[x]
.f].son[0]
==x||tr[tr[x]
.f].son[1]
==x)
)else
}//因為是無根樹,不需要記錄root
}
在link和cut操作之前,我們還要學習乙個makeroot(x)的操作
這個操作是將x變為所在整棵樹最頂層的點(也就是根)
makeroot :
inline
void makeroot (
int x )
link操作:
inline
void link (
int x ,
int y )
cut操作 :
```cpp
inline
void cut (
int x ,
int y )
對於題目中的query操作,我們引入乙個新的操作
findroot:
int find_root (
int x )
對了reverse和rotate操作還要給出
inline
void reverse (
int x )
inline
void rotate (
int x ,
int w )
完整的**:
#include
using
namespace std ;
inline
intread()
const
int n =
1e4+
100;
struct node
} tr[n]
;inline
void reverse (
int x )
inline
void rotate (
int x ,
int w )
int tmp[n]
;inline
void splay (
int x ,
int rt )
tmp[
++s]
= i ;
while
( s )
while
( tr[x]
.f!=rt &&
(tr[tr[x]
.f].son[0]
==x||tr[tr[x]
.f].son[1]
==x)
)else}}
inline
void access (
int x )
}inline
void makeroot (
int x )
inline
void link (
int x ,
int y )
inline
void cut (
int x ,
int y )
int find_root (
int x )
char s[11]
;int
main()
}return0;
}
動態樹LCT 模板
題目描述 輸入 第一行兩個整數n和m 接下來一行中n個整數表示初始點權 接下來m行每行乙個操作如上表所示。輸出 對於每乙個連線操作,若p和q不連通,輸出yes,並新增這條邊 否則輸出no 對於每乙個刪除操作,若p和q間有邊,輸出yes,並刪除這條邊,否則輸出no 對於每乙個查詢最大及查詢和,若p和q...
動態樹 LCT 錯誤總結
彙總犯過的一大堆神奇錯誤。例 node findroot node u return u 解決方法 寫完後搜尋所有 ch,檢查是否之前已pushdown 解決方法 使u uu結點懶標記意義表示u uu的兒子結點需更新,而不是u uu需要更新。懶標記下傳後未清空 例 void pushdown 例 n...
動態樹(LCT)學習筆記
candy flashhu xzyxzy 想要學lct的還是看這幾篇比較好。我這篇只是總結一些容易理解錯的或者一不小心打錯的地方。lct link cut tree 就是又可以link 動態加邊 又可以cut 動態刪邊 的維護一片森林的資料結構。lct使用實鏈剖分,對每一條實鏈用splay維護 一棵...