連結 :http://www.notonlysuccess.com/index.php/splay-tree/
論文連結:http://www.docin.com/p-62465596.html
其實本來不想學splay樹的,因為好像平時做題不怎麼用到,但是,請注意,書到用時方恨少啊,多一點儲備,就多一分機會
論文裡說,動態樹也要用splay來維護的說,有的斜率最佳化的題也要用splay來最佳化,所以,這幾天我將我找到的splay的題目刷了下,整理如下
我覺得伸展樹的作用應該分為兩類,一類主要體現在維護區間的作用上,而另一類則是和普通的平衡樹一樣
首先介紹一下普通平衡樹都有的功能
普通平衡樹的功能主要有
插入 刪除 一個數
找 前驅 後繼 第k大 小
求大於等於或小於等於某個數的個數(可以求逆序數)
確定一個數的排名
我找了一些基本的資料結構題 ,用 treap 和 splay 都實現了下,效率差不多
CSDN代碼沒有收縮功能,所以就不放這裡了
splay的普通平衡樹功能 : 模板在這裡
下面是splay最重要的功能,對區間各種性質的維護
notonlysuccess的部落格裡已經有了很詳細的介紹和一些習題,我就把做過的題貼上來吧
普通平衡樹功能,找第k大,插入刪除等 鬱悶的出納員 題解
找前驅 後繼 刪除一個節點,直接將要刪除的節點旋轉到根,然後刪除根節點即可 寵物收養所: 題解
營業額統計 題解
區間更新,求和,模板題
區間翻轉 題解
Queue-jumpers
要注意離散化的方法,其他動作對伸展樹來說就相當於類比 題解
區間切割插入 題解
上面的題都做了就可以挑戰下下面兩個重口味的題目了
poj 3580 這道題會用到伸展樹的各種操作,主要是交換兩個相鄰的區間,直接切割一個區間,然後再插入相應的位置即可 題解
維修數列 這題的最大子列和 和 區間翻轉結合起來 製造了一個巨大的坑,如果發現不了,下面這組資料可以告訴你為什麼,不過最好還是自己去發現,
6
20
259
-231 -919 241 -676 -978
MAKE-SAME
4 3 772
REVERSE
2 5
MAX-SUM
維修數列的題解在這裡
呵呵,伸展樹搞定了,可以開工其他以前可望不可即的資料結構了
伸展樹的模板
#include<cstdio>typedef __int64 lld;const int inf = ~0u>>2;#define L ch[x][0]#define R ch[x][1]#define KT (ch[ch[rt][1]][0])const int maxn = 222222;struct SplayTree{int ch[maxn][2];int pre[maxn],sz[maxn],val[maxn];int rt,top;void Rotate(int x,int f) {int y = pre[x];down(y); down(x);ch[y][!f] = ch[x][f];pre[ ch[x][f] ] = y;pre[x] = pre[y];if(pre[x]) ch[ pre[y] ][ ch[pre[y]][1] == y ] = x;ch[x][f] = y;pre[y] = x;up(y);}void Splay(int x,int goal) {down(x);while(pre[ x ] != goal ) {down( pre[pre[x]] ); down( pre[x] ); down(x);if(pre[ pre[x] ] == goal) Rotate( x , ch[ pre[x] ][0] == x ); else {int y = pre[x] ,z = pre[y];int f = ( ch[z][0] == y);if(ch[y][f] == x) Rotate( x , !f ),Rotate( x , f );else Rotate( y , f ),Rotate( x , f );}}up(x);if(goal == 0) rt = x;}void RTO(int k,int goal) {int x=rt;down(x);while(sz[ L ] + 1 != k) {if(k < sz[ L ] + 1) x = L ;else {k -= (sz[ L ] + 1);x = R;}down(x);}Splay(x,goal);}void vist(int x){if(x){printf("結點%2d : 左兒子 %2d 右兒子 %2d val: %2d sum=%I64d\n",x,ch[x][0],ch[x][1],val[x],sum[x]);vist(L);vist(R);}}void debug() {puts("");vist(rt); puts("");}void up(int x) {sz[x] = 1 + sz[ L ] + sz[ R ];sum[x] = val[x] + sum[ L ] + sum[ R ];}void down(int x) {if(add[x]) {val[ L ] += add[ x ];val[ R ] += add[ x ];add[ L ] += add[ x ];add[ R ] += add[ x ];sum[ L ] += (lld)add[x] * sz[ L ];sum[ R ] += (lld)add[x] * sz[ R ];add[ x ] = 0;}}void Newnode(int &x,int c,int f) {x=++top;L = R = 0; sz[x]=1;pre[x]=f;val[x] = sum[x] = c; add[x] = 0;}void build(int &x,int l,int r,int f) {if(l>r) return ;int m=l+r>>1;Newnode(x , num[m] , f);build( L , l , m-1 , x);build( R , m + 1, r , x);up(x);}void init(int n) { ch[0][0]=ch[0][1]=pre[0]=0;sz[0]=rt=top=0;add[0]=sum[0]=0;Newnode(rt,-1,0);Newnode(ch[rt][1],-1,rt);sz[rt]=2;for(int i=1;i<=n;i++) scanf("%d",&num[i]);build(KT,1,n,ch[rt][1]);up(ch[rt][1]); up(rt);}void update() {int l,r,c;scanf("%d%d%d",&l,&r,&c);RTO(l,0);RTO(r+2,rt);add[KT] += c;val[KT] += c;sum[KT] += (lld) c * sz[KT];}void query() {int l,r;scanf("%d%d",&l,&r);RTO(l,0);RTO(r+2,rt);printf("%I64d\n",sum[KT]);}lld sum[maxn];int add[maxn];int num[maxn];}spt;int main() {int m,n;char op[5];scanf("%d%d",&n,&m);spt.init(n);while(m--) { scanf("%s",op);if(op[0]=='Q') spt.query();else spt.update();}return 0;}/*4 101 2 3 4Q 2 4*/