很愚昧的以為動態樹是一種資料結構,現在才知道動態樹是是一類問題(Dynamic Tree Problems)。
spoj上有一系列關於樹的很有趣的題目(qtree1~4),和樹鏈剖分、動態樹有關,所以就狠下心的研究了一番,多虧找到了一篇好論文《qtree解法的一些研究 by yangzhe》,解決動態樹問題的資料結構叫link-cut tree(又是tarjan發明的,無限崇拜!!),看懂了思想以後便抱著視死如歸的心態編了起來(拿hnoi2010的bounce來練手),因為看了標程,7kb、8kb、7kb,=、=。
首先說說樹鏈剖分,差不多就是link-cut tree 的靜態版(靜態樹!?),前提是樹的形狀不發生改變。樹中的重邊的定義就是每個點向他最大的兒子(子樹大小最大)連的邊,其他的邊就是輕邊。可以證明一個點到根節點經過的輕邊數不超過logN,證明:
定義size(i)為以i為根的子樹大小,
若 i 和 fa(i) 之間連的是輕邊
也就是 存在 j∈son(fa(i)),size(j)≥size(i)
又∵ size(fa(i))≥size(i)+size(j),
∴size(fa(i))>2*size(i).
每走一條輕邊節點數都會增大一倍,那當然不可能超過logN條輕邊咯。
對於一串聯續的重邊,我們就把它壓縮為一條重路徑。
如果這棵樹的形狀不改變,那麼重路徑自然也不會發生變化,那我們可以用線段樹或者是虛二叉樹來維護,若會改變,則用splay tree維護(以深度為關鍵字)。
在實際link-cut tree的編寫中並不要考慮誰是重邊誰是輕邊,假設你訪問了某一個點,就把它到父親的邊全改為重邊就可以了,可以證明也是logN的(證明有點懵。。)。
回顧hnoi2010的彈飛綿羊,這正是一個動態樹問題(哎,我靠怎麼那時候不會捏?),要求支援這樣兩個操作:
1.詢問某個點的深度。
2.改變某個點的父親。
記得之前用塊狀鏈表水過了,速度還不錯,不過看來 link-cut tree 還是以絕對優勢壓倒了:
塊狀鏈表:
link-cut tree
這次還給了我一個教訓,不要被標程嚇到了。。。可能牛們為了讓人更容易看懂才編長一點的吧,卻著實把我這種小白嚇到了。。。
自己編起來感覺也很舒服,74行,主要注意的還是旋轉時候的父親的轉化。
(裸的splay不解釋。。。都是想想寫寫胡編亂抽的)代碼:
program link_cut_tree; const maxn=200005;<br />var<br /> root:array[0..maxn] of boolean;<br /> l,r,fa,s:array[0..maxn] of longint;<br /> n,i,task,j,z,k:longint;</p><p>procedure left(i:longint); begin<br /> z:=r[i];r[i]:=l[z];fa[r[i]]:=i;l[z]:=i;<br /> if i=l[fa[i]] then l[fa[i]]:=z else<br /> if i=r[fa[i]] then r[fa[i]]:=z; //!!<br /> fa[z]:=fa[i];fa[i]:=z;<br /> root[z]:=root[i] xor root[z];<br /> root[i]:=root[i] xor root[z];<br /> s[i]:=s[l[i]]+s[r[i]]+1;<br /> s[z]:=s[l[z]]+s[r[z]]+1;<br />end;</p><p>procedure right(i:longint); begin<br /> z:=l[i];l[i]:=r[z];fa[l[i]]:=i;r[z]:=i;<br /> if i=l[fa[i]] then l[fa[i]]:=z else<br /> if i=r[fa[i]] then r[fa[i]]:=z; //!!<br /> fa[z]:=fa[i];fa[i]:=z;<br /> root[z]:=root[i] xor root[z];<br /> root[i]:=root[i] xor root[z];<br /> s[i]:=s[l[i]]+s[r[i]]+1;<br /> s[z]:=s[l[z]]+s[r[z]]+1;<br />end;</p><p>procedure splay(i:longint); begin<br /> while not root[i] do<br /> if i=l[fa[i]] then right(fa[i]) else left(fa[i]);<br />end;</p><p>procedure access(i:longint); begin<br /> splay(i);<br /> while fa[i]<>0 do begin<br /> splay(fa[i]);<br /> z:=fa[i];<br /> root[r[z]]:=true;root[i]:=false;<br /> r[z]:=i;<br /> s[z]:=s[l[z]]+s[r[z]]+1;<br /> splay(i);<br /> end;<br />end;</p><p>begin<br /> assign(input,'input.txt');reset(input);<br /> assign(output,'output.txt');rewrite(output);<br /> readln(n);<br /> for i:=1 to n do begin<br /> read(fa[i]);inc(fa[i],i);<br /> if fa[i]>n then fa[i]:=n+1;<br /> end;<br /> for i:=1 to n+1 do s[i]:=1;<br /> fillchar(root,sizeof(root),true);<br /> readln(task);<br /> for task:=1 to task do begin<br /> read(z);<br /> if z=1 then begin<br /> readln(i);inc(i);<br /> access(i);<br /> writeln(s[l[i]]);<br /> end else begin<br /> readln(j,k);inc(j);<br /> splay(j);<br /> fa[l[j]]:=fa[j];<br /> root[l[j]]:=true;<br /> l[j]:=0;s[j]:=s[r[j]]+1;<br /> fa[j]:=j+k;<br /> if fa[j]>n then fa[j]:=n+1;<br /> end;<br /> end;<br /> close(input);close(output);<br />end.