RoNgDaZhOnG

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Splay树可以利用 我Treap树的程序中的rotate函数实现 Splay(x,S)功能,将节点x伸展的节点S处。Splay(x,S)时有三种情况:
1、zig情况。
    X是查找路径上我们需要旋转的一个非根节点。
    如果X的父节点是根,那么我们用下图所示的方法旋转X到根:
     
                                图2
    这和一个普通的单旋转相同。
2、zig-zag情况。
在这种情况中,X有一个父节点P和祖父节点G(P的父节点)。X是右子节点,P是左子节点,或者反过来。这个就是双旋转。
先是X绕P左旋转,再接着X绕G右旋转。
如图所示:
 
                            图三
3、zig-zig情况。
    这和前一个旋转不同。在这种情况中,X和P都是左子节点或右子节点。
    先是P绕G右旋转,接着X绕P右旋转。
    如图所示:
     
                                    图四
我就是zig-zag搞糊了WA了一下午。。一天比一天苦
下面附个标程:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<algorithm>
  5 using namespace std;
  6 struct Tree_Node{
  7        int ch[2],par,x,sum;
  8        bool rev_flag;
  9 }Tree[100100];
 10 int Tree_Cnt;
 11 int n,m;
 12 
 13 void Tree_Update(int v){
 14      Tree[v].sum=1;
 15      if(Tree[v].ch[0]) Tree[v].sum+=Tree[Tree[v].ch[0]].sum;
 16      if(Tree[v].ch[1]) Tree[v].sum+=Tree[Tree[v].ch[1]].sum;
 17 }
 18 void Tree_Pushdown(int v){
 19      Tree[v].rev_flag=false;
 20      int t=Tree[v].ch[0];
 21      Tree[v].ch[0]=Tree[v].ch[1];
 22      Tree[v].ch[1]=t;
 23      if(Tree[v].ch[0]) Tree[Tree[v].ch[0]].rev_flag=(!Tree[Tree[v].ch[0]].rev_flag);
 24      if(Tree[v].ch[1]) Tree[Tree[v].ch[1]].rev_flag=(!Tree[Tree[v].ch[1]].rev_flag);                       
 25 }
 26 
 27 void Tree_Rotate(int v,int k){
 28      int X=Tree[v].ch[k];
 29      
 30      int f=(Tree[Tree[v].par].ch[1]==v);
 31      Tree[Tree[v].par].ch[f]=X;
 32      Tree[X].par=Tree[v].par;
 33      Tree[v].par=X;
 34      Tree[v].ch[k]=Tree[X].ch[!k];
 35      if(Tree[X].ch[!k]) Tree[Tree[X].ch[!k]].par=v;
 36      Tree[X].ch[!k]=v;
 37      
 38      Tree_Update(v);Tree_Update(X);          
 39 }
 40 void Tree_Splay(int v,int to){
 41      if(v==to) return;
 42      int p1=Tree[v].par;
 43      int f1=(Tree[p1].ch[1]==v);
 44      if(p1==to){   
 45                 Tree_Rotate(p1,f1);
 46                 return;
 47      }
 48      int p2=Tree[p1].par;
 49      int f2=(Tree[p2].ch[1]==p1);
 50      if(f1==f2){
 51                 Tree_Rotate(p2,f1);
 52                 Tree_Rotate(p1,f1);
 53      }
 54      else{
 55           Tree_Rotate(p1,f1);
 56           Tree_Rotate(p2,f2);
 57      }
 58      if(p2==to) return;
 59      Tree_Splay(v,to);
 60 }
 61 void Tree_Ins(int par,int &v,int x){
 62      if(v==0){
 63                      Tree[v=++Tree_Cnt].x=x;
 64                      Tree[v].par=par;
 65                      Tree[v].sum=1;
 66                      Tree_Splay(v,Tree[0].ch[0]); 
 67      }
 68      else{
 69           Tree[v].sum++;
 70           int f=(x>Tree[v].x);
 71           Tree_Ins(v,Tree[v].ch[f],x);
 72      }
 73 }
 74 int Tree_Find(int v,int x){
 75      if(Tree[v].rev_flag) Tree_Pushdown(v);
 76      int mid=1;
 77      if(Tree[v].ch[0]) mid+=Tree[Tree[v].ch[0]].sum;
 78      if(x==mid){Tree_Splay(v,Tree[0].ch[0]);return v;}  
 79      if(x<mid) return Tree_Find(Tree[v].ch[0],x);
 80      if(x>mid) return Tree_Find(Tree[v].ch[1],x-mid); 
 81 }
 82 
 83 void Tree_Rev(int x,int y){
 84      x++,y++;
 85      int v1=Tree_Find(Tree[0].ch[0],x-1);
 86      int v2=Tree_Find(Tree[0].ch[0],y+1);
 87      
 88      Tree_Splay(v1,Tree[0].ch[0]);
 89      Tree_Splay(v2,Tree[Tree[0].ch[0]].ch[1]);
 90      int t=Tree[Tree[Tree[0].ch[0]].ch[1]].ch[0];
 91      Tree[t].rev_flag=(!Tree[t].rev_flag);
 92 }                      
 93 void Print_Out(int v){
 94      if(Tree[v].rev_flag) Tree_Pushdown(v);
 95      if(Tree[v].ch[0]) Print_Out(Tree[v].ch[0]);
 96      if(Tree[v].x>=1 && Tree[v].x<=n) printf("%d ",Tree[v].x);
 97      if(Tree[v].ch[1]) Print_Out(Tree[v].ch[1]);
 98 } 
 99 
100 int main(){
101     scanf("%d%d",&n,&m);
102     for(int i=1;i<=n;i++) Tree_Ins(0,Tree[0].ch[0],i);
103     Tree_Ins(0,Tree[0].ch[0],0);Tree_Ins(0,Tree[0].ch[0],n+1);
104     
105     int x,y;
106     for(int i=0;i<m;i++){
107             scanf("%d%d",&x,&y);
108             Tree_Rev(x,y);
109     }
110     Print_Out(Tree[0].ch[0]);
111     return 0;
112 }

PS:程序中用Tree[0].ch[0]表示根节点,这样便于操作。每次插入和查找等操作都要Splay一次,这样才能维护Splay树的均摊时间复杂度,不至于超时,要翻转一个区间时只需把这个区间之前的一个元素Splay到根节点,之后的一个元素Splay到根节点的右儿子,则根节点的右儿子的左二子即为所求区间,打上翻转标记即可。此题n<=10 0000,当n=10 0000时Tree_Ins函数的递归调用在windows下会爆栈,但好在评测机是Linux,栈大点。。于是没爆。Linux的栈大小一般为8M。

posted on 2017-03-16 22:11  学无止境-1980  阅读(72)  评论(0编辑  收藏  举报