by:
動態樹是一類要求維護森林的連通性的題的總稱,這類問題要求維護某個點到根的某些資料,支援樹的切分,合併,以及對子樹的某些操作。其中解決這一問題的某些簡化版(不包括對子樹的操作)的基礎資料結構就是lct(link-cut tree)。
lct的大體思想類似於樹鏈剖分中的輕重鏈剖分,輕重鏈剖分是處理出重鏈來,由於重鏈的定義和樹鏈剖分是處理靜態樹所限,重鏈不會變化,變化的只是重鏈上的邊或點的權值,由於這個性質,我們用線段樹來維護樹鏈剖分中的重鏈,但是lct解決的是動態樹問題(包含靜態樹),所以需要用更靈活的splay來維護這裡的「重鏈」
定義:首先來定義一些量:
access(x):表示訪問x點(之後會有說明)。
preferred child(偏愛子節點):如果最後被訪問的點在x的兒子p節點的子樹中,那麼稱p為x的preferred child,如果乙個點被訪問,他的preferred child為null(即沒有)。
preferred edge(偏愛邊):每個點到自己的preferred child的邊被稱為preferred edge。
preferred path(偏愛路徑):由preferred edge組成的不可延伸的路徑稱為preferred path。
這樣我們可以發現一些比較顯然的性質,每個點在且僅在一條preferred path上,也就是所有的preferred path包含了這棵樹上的所有的點,這樣一顆樹就可以由一些preferred path來表示(類似於輕重鏈剖分中的重鏈),我們用splay來維護每個條preferred path,關鍵字為深度,也就是每棵splay中的點左子樹的深度都比當前點小,右節點的深度都比當前節點
的深度大。這樣的每棵splay我們稱為auxiliary tree(輔助樹),每個auxiliary tree的根節點儲存這個auxiliary tree與上一棵auxiliary tree中的哪個點相連。這個點稱作他的path parent。
看乙個例子
粗的邊是preferred path。那麼3-7這個auxiliary tree中,path parent為1節點,每個單獨的點單獨在一棵splay中。以上描述的幾個量可以儲存這棵樹,並且維護相應的資訊。
操作:access(x):首先由於preferred path的定義,如果乙個點被訪問,那麼這個點到根節點的所有的邊都會變成preferred edge,由於每個點只有乙個preferred child,所以這個點到根節點路徑上的所有的點都會和原來的preferred child斷開,連線到這條新的preferred path上。假設訪問x點,那麼先將x點旋轉到對應auxiliary tree的根節點,然後因為被訪問的點是沒有preferred child的,所以將auxiliary tree中根節點(x)與右子樹的邊斷掉,左節點保留,將這個樹的path parent旋轉到對應auxiliary tree的根節點,斷掉右子樹,連線這個點與x點,相當於合併兩棵auxiliary tree,不斷地重複這一操作,直到當前x所在auxiliary tree的path parent為null時停止,表示已經完成當前操作。
procedure
access
(x:longint);
var y :longint;
begin
splay(x);//旋轉
while father[x]<>0
dobegin
y:=father[x];
splay(y);
root[son[y,1]]:=true;//son為子節點son[x,0]代表左子結點,son[x,1]代表右子結點
root[x]:=false;//當前點是否為對應auxiliary tree的根節點
son[y,1]:=x;
update(y);//更新y點的資訊
splay(x);
end;
end;
find root(x):找到某一點所在樹的根節點(維護森林時使用)。只需要access(x),然後將x節點旋到對應auxiliary tree的根節點,然後找到這個auxiliary tree中最左面的點。
function
find
root
(x:longint)
:longint;
begin
access(x);
splay(x);//將x旋轉到根節點
exit(find(x,-maxlongint));//找到子樹中最左面的點
end;
cut(x):斷掉x節點和其父節點相連的邊。首先access(x),然後將x旋轉到對應auxiliary tree的根節點,然後斷掉auxiliary tree中x和左節點相連的邊。
procedure
cut(x:longint);
begin
access(x);
splay(x);//旋轉x點到根節點
father[son[x,0]]:=0;
root[son[x,0]]:=true;//設定左子樹根節點
son[x,0]:=-1;
end;
link(join)(x,y):連線點x到y點上。即讓x稱為y的子節點。因為x為y的子節點後,在原x的子樹中,x點到根節點的所有的點的深度會被翻轉過來,所以先access(x),然後在對應的auxiliary tree中將x旋轉到根節點,,然後將左子樹翻轉(splay中的reverse操作),然後access(y),將y旋轉到對應auxiliary tree中的根節點,將x連到y就行了。這裡寫**片
procedure
link
(x,y:longint);
begin
access(x);
splay(x);
reverse(son[x,0]);
access(y);
splay(y);
son[y,1]:=x;
father[x]:=y;
root[x]:=false;
end;
access操作是lct的基礎,應該熟練掌握並且理解。
時間複雜度:
證明access以及其他操作的時間複雜度是均攤log2n的,具體證明參考楊哲的**《qtree 解法的一些研究》。
動態索引之B樹和B 樹
目的 是一種平衡的多分樹 balanced tree 根結點至少有兩個子結點 所有的葉結點在同一層 有 k 個子結點的非根結點恰好包含k 1個關鍵碼 注意比k小1 樹高平衡,所有葉結點都在同一層 關鍵碼沒有重複,父結點中的關鍵碼是其子結點的分界 b 樹把 值接近 相關記錄放在同乙個磁碟頁中,從而利用...
動態樹之(霧)樹鏈剖分
這貨是不是動態樹里的我就不清楚了,fhq的blog好像有提到orz 一些不需要link cut操作的樹上路徑的題可以用樹鏈剖分做,常數比lct小多了。upd 所以這已經不是動態樹了囧。標題我就不改了。還好原來機智打了個 霧 學習了下hld 樹鏈剖分 嗯,挺簡單的。hld可以在樹中的操作有很多,hld...
藍橋杯 生命之樹 動態規劃DFS
生命之樹 在x森林裡,上帝建立了生命之樹。他給每棵樹的每個節點 葉子也稱為乙個節點 上,都標了乙個整數,代表這個點的和諧值。上帝要在這棵樹內選出乙個非空節點集s,使得對於s中的任意兩個點a,b,都存在乙個點列 使得這個點列中的每個點都是s裡面的元素,且序列中相鄰兩個點間有一條邊相連。在這個前提下,上...