伸展樹是一枚二叉樹,可以維護一個數列,或者可以作為二叉搜索樹,因為無論怎么旋轉,它的中序遍歷是不變的。
1、伸展操作。
Splay(x,goal):旋轉結點x,使它成為結點goal的兒子。
至於旋轉,本質上只有左旋和右旋。
2、插入。
如果作為二叉搜索樹,則插入與二叉搜索樹一樣,最后把該結點旋轉到根。
如果用於維護一個數列,要求在x位置之前插入val(val可以為一個數字或一個序列),則將第x-1個結點旋轉到根,將第x個結點旋轉到第x-1個結點的兒子,那么根據中序遍歷,val應插入在第x個結點的左兒子。
3、刪除。
與插入同理。
4、Select(k)。
在二叉搜索樹中,對每個結點維護一個num值,如果x左兒子的num+1=k,x就是第k小的數。
在維護序列中,顯然就是第k個數。
5、翻轉。
區間:將第a-1個數旋轉到根,將第b+1個數旋轉到a-1的兒子,那么b+1的左子樹就是區間[a,b]。
在b+1的左兒子標記翻轉,像線段樹一樣延遲標記,延遲標記往下推的時候交換左右兒子。
6、循環平移。
序列:1、2、3 --循環左移一位后-> 2、3、1。
本質上就是兩個區間的交換。
坑爹的地方:
1、下標為0的結點作為邊界,它的pre、next值被亂搞沒關系,但是它維護的數值(比如 num,max,min...)不能影響到要維護的數列。
2、區間翻轉,則左右兒子交換,可能左右兒子維護的值也需要交換。
3、不斷的刪除和插入一段序列時,可能會爆內存,刪除的時候只好把刪除的結點壓到棧里,循環利用。
4、區間操作時,可以在序列的兩端加入兩個邊界結點1和n+2。避免討論,但它維護的數值不能影響到要維護的數列。
數據有問題,用Splay實現很裸的二叉搜索樹。
依然是Splay實現二叉搜索樹。
依然是Splay的操作。如果在平衡樹中維護一段區間,蛋就碎了。
I命令:立刻離開不算入離開公司的員工的總數。
可以把數據分成兩部分,在Splay中的部分,和待加入平衡樹的部分。
把Splay中的數據同時加上val(val可能為負),相當於把待加入Splay的部分同時減去val,因為兩個部分數據的相對值不變。
S命令:刪除Splay中小於相對標准的子樹。
Splay維護區間最值。
線段樹是一個區間平分為兩個子區間。
Splay根據中序遍歷,把一個區間分為左、中、右。
維護上大同小異。
刪除、插入、翻轉,之前扯淡過了。
修改、求和、最大連續子序列和、都和線段樹一樣,翻轉的時候左右連續的最大和要交換。
循環移動T次,T>0表示左移,T<0表示右移。取模以后都轉換成左移即可。
很容易想到,5在1內可以把5作為1的兒子,那么可以得到一片森林,而且兒子可能很多。
那么可以用動態樹,其實用Splay也可以做到。
那么建樹完DFS一遍,可以得到一個或者多個括號序列。比如2在1內,3在1內,4在3內就是1 2 2 3 4 4 3 1。
接下來,可以用Splay維護多個序列。
詢問root的時候,就是該序列的最左端的值。
移動的時候,就是把一段序列移到另一段序列中。
給出一個無向圖,圖中每個頂點都有一個權值:
1、刪除一條邊;
2、詢問與頂點x連通的頂點中,第k大的值。
3、改變某個頂點的權值。
輸出所有詢問的平均值。
膜拜ghnjk大神的做法和他的AVL……
由於刪一條邊不能確定該圖是否邊分成兩個連通分量,但是加入一條邊可以判斷是否把兩個連通分量連通、或者還是一個連通分量(並查集)。
所以離線讀入所有詢問,把每個頂點的權值存入該頂點的鄰接表。
初始時,每個頂點都是一個單獨的Splay。
當合並兩個集合時,將兩枚Splay合並,由於兩枚Splay的值不會總是嚴格大於對方,所以只好把小的Splay的每個頂點插入到大的Splay中。