首先推荐一篇文章,http://www.notonlysuccess.com/index.php/splay-tree/ 这个是胡浩大牛的文章,推荐原因很简单!第一,上面有很多经典论文,第二,有很多经典题目。故推荐
胡浩的文章中提到的那几篇论文中已经将伸展树将的非常清楚了!我说说我的感受,伸展树是一种非常灵活的数据结构,原因就是,伸展树可以将你想要的点转移到任意一个地方,这样的话,添加、删除、修改、区间操作都比较轻松了。
有 几点自己的总结:
删除节点:方法一,将其移动到根,将根的前驱移动到根的左儿子,这样就可以用根的前驱替换掉根。方法二,可以讲前驱移动到根,将后继移动到根的右儿子,这样的话,根右儿子的左儿子就是要删除的点,所有的区间操作几乎都用这个。
手动回收内存:这个胡浩代码中有这方面的知识,不细说,想想也挺简单的。但是,一般给定的内存,int可以开到9*10^6而时间几乎也就卡到10^6到10^8,这样的话,内存和时间几乎是同步的,竞赛中还用回收内存么??我觉得几乎就不用,没遇到过。
开头和结尾:伸展树中很多时候都需要前驱和后继,可是最后一个的后继,最前的前驱怎么弄呢?所以在伸展树中添加了一个开始节点和最后的节点,添加这两个几乎不影响任何操作,只是计数的时候记得自己添加过就是了。
RotateTo:这个方法实现的是将第k个旋转到goal下。当然前面提到的前驱,这儿方法中一定给去掉了,也就是k=1就是你要的第一个元素。要特别注意。
注意:伸展操作是,x必须在goal的子树中!!
Robotic Sort 伸展树题目,伸展树的区间翻转功能
解题思路:首先将最开始的位置大小映射到一个数组中,这个数组的作用就是可以知道每一次要输出的最小值是伸展树中的哪一个!然后建立一颗伸展树,树中不用存储任何信息。接下来进行翻转和查询操作。胡浩说的比较清楚了,但是我当时理解了好久:比较有意思的题目,结点处没有任何权值,只要记录每个数字对应的结点位子,然后从小到打把相对应的位子旋转到根节点,输出i+左子树的个数,接着给左子树一个翻转的延迟标记,最后删除该节点.注意把结点旋到根部的时候要先从根部把延迟标记push_down下去.
Code:
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define keyTree (ch[ ch[root][1] ][0])
const int N = 100100;
//int num[N],val[maxn],over[N];
int look[N];
struct SplayTree
{
int over[N];
int sz[N],ch[N][2],f[N], root, top;
inline void zig(int x){
int y=f[x], z=f[y];
ch[y][1]=ch[x][0]; f[ch[x][0]]=y;
ch[x][0]=y; f[y]=x;
f[x]=z;
if(z) ch[z][ch[z][1]==y]=x;
Push_up(y);
}
inline void zag(int x){
int y=f[x], z=f[y];
ch[y][0]=ch[x][1]; f[ch[x][1]]=y;
ch[x][1]=y; f[y]=x;
f[x]=z;
if(z) ch[z][ch[z][1]==y]=x;
Push_up(y);
}
inline void zigzig(int x){
int y=f[x], z=f[y], fz=f[z];
ch[z][1]=ch[y][0]; f[ch[y][0]]=z;
ch[y][1]=ch[x][0]; f[ch[x][0]]=y;
f[z]=y; ch[y][0]=z;
f[y]=x; ch[x][0]=y;
f[x]=fz;
if(fz) ch[fz][ch[fz][1]==z]=x;
Push_up(z); Push_up(y);
}
inline void zagzag(int x){
int y=f[x], z=f[y], fz=f[z];
ch[z][0]=ch[y][1]; f[ch[y][1]]=z;
ch[y][0]=ch[x][1]; f[ch[x][1]]=y;
f[z]=y; ch[y][1]=z;
f[y]=x; ch[x][1]=y;
f[x]=fz;
if(fz) ch[fz][ch[fz][1]==z]=x;
Push_up(z); Push_up(y);
}
inline void zigzag(int x){
int y=f[x], z=f[y], fz=f[z];
ch[y][1]=ch[x][0]; f[ch[x][0]]=y;
ch[z][0]=ch[x][1]; f[ch[x][1]]=z;
f[y]=f[z]=x;
ch[x][0]=y; ch[x][1]=z;
f[x]=fz;
if(fz) ch[fz][ch[fz][1]==z]=x;
Push_up(z); Push_up(y);
}
inline void zagzig(int x){
int y=f[x], z=f[y], fz=f[z];
ch[y][0]=ch[x][1]; f[ch[x][1]]=y;
ch[z][1]=ch[x][0]; f[ch[x][0]]=z;
f[y]=f[z]=x;
ch[x][1]=y; ch[x][0]=z;
f[x]=fz;
if(fz) ch[fz][ch[fz][1]==z]=x;
Push_up(z); Push_up(y);
}
void splay(int x, int goal){
int y, z;
Push_down(x);
while(f[x]!=goal){
if(f[f[x]]==goal){
y=f[x]; Push_down(y); Push_down(x);
if(ch[y][1]==x) zig(x);
else zag(x);
}
else{
y=f[x]; z=f[y];
Push_down(z); Push_down(y); Push_down(x);
if(ch[z][1]==y){
if(ch[y][1]==x) zigzig(x);
else zagzig(x);
}
else{
if(ch[y][0]==x) zagzag(x);
else zigzag(x);
}
}
}
Push_up(x);
if(f[x]==0) root=x;
}
inline void RotateTo(int k,int goal)//将第k个移动到goal
{
int x = root;Push_down(x);
while(sz[ ch[x][0] ] != k)
{
if(k < sz[ ch[x][0] ]) x = ch[x][0];
else k -= (sz[ ch[x][0] ] + 1) ,x = ch[x][1];
Push_down(x);
}
splay(x,goal);
}
inline void erase(int x) {}//把以x为祖先的节点删除放进内存池,这个地方没写!!
///pppppppppppppppppppppppppppp//
inline void newNode(int &x)
{
x = ++top;
ch[x][0] = ch[x][1] = f[x] = over[x] = 0;
sz[x] = 1;
}
inline void Push_down(int x)
{
if(over[x])
{
int t = ch[x][0];ch[x][0] = ch[x][1];ch[x][1] = t;
over[ ch[x][0] ] ^= 1;
over[ ch[x][1] ] ^= 1;
over[x] = 0;
}
}
inline void Push_up(int x)
{
sz[x] = 1 + sz[ ch[x][0] ] + sz[ ch[x][1] ];
}
inline void makeTree(int &x,int l,int r,int fa)
{
if(l > r) return;
int m = (l+r) >> 1;
newNode(x);
look[m] = x;
makeTree(ch[x][0],l,m-1,x);
makeTree(ch[x][1],m+1,r,x);
f[x] = fa;
Push_up(x);
}
inline void init(int n)
{
memset(look,0,sizeof(look));
root = top = 0;
ch[0][0]=ch[0][1]=f[0]=sz[0]=over[0]=0;
//为了方便处理边界,加两个边界顶点
newNode(root );
newNode(ch[root][1] );
f[top] = root;
sz[root] = 2;
makeTree(root,0,n-1,0);
}
inline int fe(int x)
{
Push_down(x);
while(ch[x][1])
{
x = ch[x][1];
Push_down(x);
}
return x;
}
inline void delRoot(int x)
{
if(ch[root][0]==0){
root=ch[root][1];
f[root]=0;
}
else{
int m=fe(ch[root][0]);
splay(m, root);
ch[m][1]=ch[root][1];
f[ch[root][1]]=m;
root=m;
f[root]=0;
Push_up(root);
}
}
inline int query(int k)
{
int x = look[k],ans;
splay(x,0);
ans = sz[ ch[x][0] ] + 1;
over[ ch[x][0] ] ^= 1;
//debug();
delRoot(x);
return ans;
}
/*(void debug()
{
cout <<"--------------debug------------\n";
cout << root << "\n";
outtree(root);
}
void outtree(int ro)
{
if(ro == 0) return;
outtree(ch[ro][0]);
cout << "根:" << ro << " 左:" << ch[ro][0] << " 右:" << ch[ro][1] << " 大:" << sz[ro] << " " << "\n";
outtree(ch[ro][1]);
}
*/
}tree;
struct note
{
int value,pos;
}p[N];
bool cmp(const note a,const note b)
{ return a.value < b.value || (a.value == b.value && a.pos < b.pos);}
int main()
{
int n;
while(scanf("%d",&n) && n != 0)
{
for(int i = 0;i < n;i++)
{
scanf("%d",&p[i].value);
p[i].pos = i;
}
sort(p,p+n,cmp);
tree.init(n);
//tree.debug();
for(int i = 0;i < n;i++)
{
if(i != 0) printf(" ");
printf("%d",tree.query(p[i].pos)+i );
//tree.debug();
}
printf("\n");
}
return 0;
}
Play with Chain
此题有splay的最独特的操作 区间旋转和切割
我的代码中有很多这个题中没有用到的,我也加上了!
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define keyTree (ch[ ch[root][1] ][0])
const int N = 300100;
//int num[N],val[maxn],over[N];
bool has = 0;
struct SplayTree
{
int over[N];
int sz[N],ch[N][2],f[N], root, top;
///ppp
int val[N];
///-----pp----
inline void zig(int x){
int y=f[x], z=f[y];
ch[y][1]=ch[x][0]; f[ch[x][0]]=y;
ch[x][0]=y; f[y]=x;
f[x]=z;
if(z) ch[z][ch[z][1]==y]=x;
Push_up(y);
}
inline void zag(int x){
int y=f[x], z=f[y];
ch[y][0]=ch[x][1]; f[ch[x][1]]=y;
ch[x][1]=y; f[y]=x;
f[x]=z;
if(z) ch[z][ch[z][1]==y]=x;
Push_up(y);
}
inline void zigzig(int x){
int y=f[x], z=f[y], fz=f[z];
ch[z][1]=ch[y][0]; f[ch[y][0]]=z;
ch[y][1]=ch[x][0]; f[ch[x][0]]=y;
f[z]=y; ch[y][0]=z;
f[y]=x; ch[x][0]=y;
f[x]=fz;
if(fz) ch[fz][ch[fz][1]==z]=x;
Push_up(z); Push_up(y);
}
inline void zagzag(int x){
int y=f[x], z=f[y], fz=f[z];
ch[z][0]=ch[y][1]; f[ch[y][1]]=z;
ch[y][0]=ch[x][1]; f[ch[x][1]]=y;
f[z]=y; ch[y][1]=z;
f[y]=x; ch[x][1]=y;
f[x]=fz;
if(fz) ch[fz][ch[fz][1]==z]=x;
Push_up(z); Push_up(y);
}
inline void zigzag(int x){
int y=f[x], z=f[y], fz=f[z];
ch[y][1]=ch[x][0]; f[ch[x][0]]=y;
ch[z][0]=ch[x][1]; f[ch[x][1]]=z;
f[y]=f[z]=x;
ch[x][0]=y; ch[x][1]=z;
f[x]=fz;
if(fz) ch[fz][ch[fz][1]==z]=x;
Push_up(z); Push_up(y);
}
inline void zagzig(int x){
int y=f[x], z=f[y], fz=f[z];
ch[y][0]=ch[x][1]; f[ch[x][1]]=y;
ch[z][1]=ch[x][0]; f[ch[x][0]]=z;
f[y]=f[z]=x;
ch[x][1]=y; ch[x][0]=z;
f[x]=fz;
if(fz) ch[fz][ch[fz][1]==z]=x;
Push_up(z); Push_up(y);
}
void splay(int x, int goal){
int y, z;
Push_down(x);
while(f[x]!=goal){
if(f[f[x]]==goal){
y=f[x]; Push_down(y); Push_down(x);
if(ch[y][1]==x) zig(x);
else zag(x);
}
else{
y=f[x]; z=f[y];
Push_down(z); Push_down(y); Push_down(x);
if(ch[z][1]==y){
if(ch[y][1]==x) zigzig(x);
else zagzig(x);
}
else{
if(ch[y][0]==x) zagzag(x);
else zigzag(x);
}
}
}
Push_up(x);
if(f[x]==0) root=x;
}
inline void RotateTo(int k,int goal)//将第k个移动到goal 这个地方的k小没有算添加的那个最小的!!
{
int x = root;Push_down(x);
while(sz[ ch[x][0] ] != k)
{
if(k < sz[ ch[x][0] ]) x = ch[x][0];
else k -= (sz[ ch[x][0] ] + 1) ,x = ch[x][1];
Push_down(x);
}
splay(x,goal);
}
inline void erase(int x) {}//把以x为祖先的节点删除放进内存池,这个地方没写!!
///pppppppppppppppppppppppppppp//
inline void newNode(int &x,int v)
{
x = ++top;
ch[x][0] = ch[x][1] = f[x] = over[x] = 0;
val[x] = v;
sz[x] = 1;
}
inline void Push_down(int x)
{
if(over[x])
{
int t = ch[x][0];ch[x][0] = ch[x][1];ch[x][1] = t;
over[ ch[x][0] ] ^= 1;
over[ ch[x][1] ] ^= 1;
over[x] = 0;
}
}
inline void Push_up(int x)
{
sz[x] = 1 + sz[ ch[x][0] ] + sz[ ch[x][1] ];
}
inline void makeTree(int &x,int l,int r,int fa)
{
if(l > r) return;
int m = (l+r) >> 1;
newNode(x,m);
makeTree(ch[x][0],l,m-1,x);
makeTree(ch[x][1],m+1,r,x);
f[x] = fa;
Push_up(x);
}
inline void init(int n)
{
root = top = 0;
ch[0][0]=ch[0][1]=f[0]=sz[0]=over[0]=0;
//为了方便处理边界,加两个边界顶点
newNode(root ,-1);
newNode(ch[root][1] ,-1);
f[top] = root;
sz[root] = 2;
makeTree(keyTree,1,n,ch[root][1] );
}
inline int fe(int x)
{
Push_down(x);
while(ch[x][1])
{
x = ch[x][1];
Push_down(x);
}
return x;
}
inline void delRoot(int x)
{
if(ch[root][0]==0){
root=ch[root][1];
f[root]=0;
}
else{
int m=fe(ch[root][0]);
splay(m, root);
ch[m][1]=ch[root][1];
f[ch[root][1]]=m;
root=m;
f[root]=0;
Push_up(root);
}
}
/*inline int query(int k)
{
int x = look[k],ans;
splay(x,0);
ans = sz[ ch[x][0] ] + 1;
over[ ch[x][0] ] ^= 1;
//debug();
delRoot(x);
return ans;
}
*/
inline int select(int ro,int k)//返回第k个的地址
{
if(sz[ ch[ro][0] ] + 1 == k) return ro;
if(sz[ ch[ro][0] ] >= k) return select(ch[ro][0],k);
return select(ch[ro][1], k - 1 - sz[ ch[ro][0] ]);
}
inline void change(int a,int b,int c)//注意c==0的情况
{
//由于前边加了一个 所以应该是将第a+1到b+1个查到第c个后面
//splay(pree(,0);
//splay(,root);
RotateTo(a-1,0);
RotateTo(b+1,root);
int x = keyTree;
int tt = ch[root][1];
ch[tt][0] = 0;
Push_up(tt);
Push_up(root);
///上面将a到b删掉了
if(c == 0)///这个地方由于RotateTo是从1开始的!
splay(1,0);
else
RotateTo(c,0);
RotateTo(c+1,root);
tt = ch[root][1];
ch[tt][0] = x;
f[x] = tt;
Push_up(tt);
Push_up(root);
}
inline void flip(int a,int b)
{
RotateTo(a-1,0);
RotateTo(b+1,root);
over[ch[ ch[root][1] ][0] ] ^= 1;
}
inline void out(int root)
{
Push_down(root);
if(root == 0) return;
out(ch[root][0]);
if(val[root] != -1)
{
if(has) printf(" ");
has = 1;
printf("%d",val[root]);
}
out(ch[root][1]);
}
void debug()
{
cout <<"--------------debug------------\n";
cout << root << "\n";
outtree(root);
}
void outtree(int ro)
{
Push_down(ro); //这个!!
if(ro == 0) return;
outtree(ch[ro][0]);
cout << "根:" << ro << " 左:" << ch[ro][0] << " 右:" << ch[ro][1] << " 大:" << sz[ro] << " " << val[ro] <<"\n";
outtree(ch[ro][1]);
}
}tree;
/**
*涉及两个操作,区间翻转,和子树合并
*/
int main()
{
int n,m;
while(scanf("%d%d",&n ,&m) && !(n == -1 && m == -1) )
{
tree.init(n);
char str[10];
int a,b,c;
//tree.debug();
for(int i = 0;i < m;i++)
{
scanf("%s",str);
if(str[0] == 'C')//cut
{
scanf("%d%d%d",&a,&b,&c);
tree.change(a,b,c);
}else//flip
{
scanf("%d%d",&a,&b);
tree.flip(a,b);
}
//tree.debug();
}
has = 0;
tree.out(tree.root);
printf("\n");
}
return 0;
}