链接 :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大,插入删除等 郁闷的出纳员 题解
找前驱 后继 删除一个节点,直接将要删除的节点旋转到根,然后删除根节点即可 宠物收养所: 题解
营业额统计 题解
区间更新,求和,模板题
区间翻转 题解
区间切割插入 题解
上面的题都做了就可以挑战下下面两个重口味的题目了
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 10 1 2 3 4 Q 2 4 */