【模板Splay】XX树

来源:互联网 发布:java 接口中静态方法 编辑:程序博客网 时间:2024/06/06 00:10

话说 大概半年前专门去学了下Splay,在bzoj上写了几道题
但是,当时觉得自己很菜,想多学点东西,再一次性写出来,然后。。。。
我现在都忘掉了自己还会这个 mmp。
要讲Splay,那就必然需要了解一下旋转操作。
http://www.cnblogs.com/kuangbin/archive/2012/10/07/2714068.html
先去研究一下上述博客知识,学一下旋转操作的原理! 很重要
然后我找到了黄学长的Splay 模板,全数组构成,哇,真是好开心啊,收藏收藏:
http://hzwer.com/2841.html

以下的代码 基本都是按照kuangbin 的代码风格来写的

我的理解:
我们构造一颗二叉排序树,然后我们所有的一切操作:插入、删除、反转、旋转 都是建立在 维护这棵二叉排序树上的。但是我们要搞清楚的一点是:这棵排序树并不是以大小来排序的,而是以 我们要处理的数组 的下标来排序。 所以我们一直都在维护这个数组的相对顺序而不是大小

旋转 Rotate :有三种情况需要讨论 : (PS:kuangbin 博客的 单旋图貌似是有问题的,很明显的相对顺序都没有维护)
1. 节点x的父节点y是根节点。
这里写图片描述
2 Zig-Zig或Zag-Zag操作
节点x的父节点y不是根节点,且x与y同时是各自父节点的左孩子或者同时是各自父节点的右孩子。
这里写图片描述
3.Zig-Zag或Zag-Zig操作:节点x的父节点y不是根节点,x与y中一个是其父节点的左孩子而另一个是其父节点的右孩子。
这里写图片描述
我们可以看到这三种情况的旋转我们都将X旋转至根节点。 并且我们同时维护了他们的相对顺序大小。

我们如何实现上述的操作?:

通过入门题来讲解讲解:
POJ 3468 成段更新+区间求和
* 题目给定了n个数A1,A2,…An,有以下两种操作
* C a b c:把c加入到Aa,Aa+1,..Ab中
* Q a b:查询Aa,Aa+1,..Ab的和
开的空间:

#define key_value ch[ch[root][1]][0]   //经常利用这个#define N 200005const int INF=0x3f3f3f3f;int pre[N],ch[N][2],key[N],size[N],add[N],rev[N],m[N];/*pre:父节点   ch[N][0][1]左节点和右节点。 size子树规模,root树的当前的根 , key:这个节点代表的值  add增量延迟标记   m记录子树和。 */int root,tot;int n;int a[N];void newnode(int &r,int father,int k){  //注意r 需要引用     r=++tot;  //可知我们的根节点从1开始     pre[r]=father;  //更新 pre     size[r]=1;     //新开节点只有自己     add[r]=rev[r]=0; // 标记初识为0     ch[r][0]=ch[r][1]=0;     key[r]=m[r]=k;  }void push_rev(int r){  //反转操作    if(r){        swap(ch[r][0],ch[r][1]);  //把当前r的左右子树反转,并且把反转传递下去。        rev[r]^=1;    }}void up_add(int r,int d){  // 给根为r的子树增加值,一定要先把当前节点的标记更新掉,然后再加上延迟标记!    if(r==0) return;    add[r]+=d;    key[r]+=d;    m[r]+=d;}void push_down(int r){  //将lazy 标记更新到孩子节点    if(rev[r]){        push_rev(ch[r][0]);        push_rev(ch[r][1]);        rev[r]=0;    }    if(add[r]){        up_add(ch[r][0],add[r]);        up_add(ch[r][1],add[r]);        add[r]=0;    }}void push_up(int r){  //向上更新    size[r]=size[ch[r][0]] + size[ch[r][1]] +1;    m[r]=key[r];    if(ch[r][0])        m[r]=min(m[ch[r][0]] , m[r]);  // 因为我们的删除就是归零,所以要判断是否存在    if(ch[r][1])        m[r]=min(m[ch[r][1]] , m[r]);}void build(int &x,int l,int r,int father){  ////先建立中间结点,再两端的方法    if(l>r) return;    int mid=(l+r)>>1;    newnode(x,father,a[mid]);  //key=a[mid], 我们按照数组的下标来建树    build(ch[x][0],l,mid-1,x); // x =father    build(ch[x][1],mid+1,r,x);    push_up(x);}void init(){    //kuangbin一惯的风格init()     tot=root=0;        ch[root][0]=ch[root][1]=pre[root]=size[root]=rev[root]=0;     //root 地址为0,所以不需要赋m[root]的值     /*开一个理论上永远最大的根,和永远最小的根,这样操作有套路*/     newnode(root,0,INF);  // 这个点是永远最小的     newnode(ch[root][1],root,INF); //这个点永远最大。     build(key_value,1,n,ch[root][1]);  //建树     push_up(ch[root][1]);  //记得建完树之后更新到root     push_up(root);}void Rotate(int x,int kind)//对X旋转,0为左旋,1为右旋  该部分基本固定{    int y=pre[x];      push_down(y); //正确的顺序先传递y,然后再传递x    push_down(x);    ch[y][!kind]=ch[x][kind];    pre[ch[x][kind]]=y;    if(pre[y])        ch[pre[y]][ch[pre[y]][1]==y]=x;    pre[x]=pre[y];    ch[x][kind]=y;    pre[y]=x;    push_up(y);}void Splay(int r,int goal)  ////Splay调整,将结点r调整到goal下面{    push_down(r);    while(pre[r]!=goal)    {        if(pre[pre[r]]==goal)        {            //这题有反转操作,需要先push_down,在判断左右孩子            push_down(pre[r]);            push_down(r);            Rotate(r,ch[pre[r]][0]==r);        }        else        {            //这题有反转操作,需要先push_down,在判断左右孩子            push_down(pre[pre[r]]);            push_down(pre[r]);            push_down(r);            int y=pre[r];            int kind=(ch[pre[y]][0]==y);            //两个方向不同,则先左旋再右旋            if(ch[y][kind]==r)            {                Rotate(r,!kind);                Rotate(r,kind);            }            //两个方向相同,相同方向连续两次            else            {                Rotate(y,kind);                Rotate(r,kind);            }        }    }    push_up(r);    if(goal==0)root=r;}int Get_Kth(int r,int k) //我们得到的是第k个节点的位置。但是我们注意init的时候添加两个极大极小点。{    Push_Down(r);    int t=size[ch[r][0]]+1;// 我们添加过一个很小的点。    if(t==k)return r;    if(t>k)return Get_Kth(ch[r][0],k);    else return Get_Kth(ch[r][1],k-t);}int Get_Min(int r)  //找到最值{    Push_Down(r);    while(ch[r][0])    {        r=ch[r][0];        Push_Down(r);    }    return r;}int Get_Max(int r){    Push_Down(r);    while(ch[r][1])    {        r=ch[r][1];        Push_Down(r);    }    return r;}void ADD(int l,int r,int D){    Splay(get_kth(root,l),0);        //l-1    Splay(get_kth(root,r+2),root);   //r+1    up_add(key_value,D);    push_up(ch[root][1]);    push_up(root);}void REVERSE(int l,int r){     Splay(get_kth(root,l),0); //将第l-1个节点调制0     Splay(get_kth(root,r+2),root); //将r+1旋转至新的root,那么key_value就是(l-1,r+2)     push_rev(key_value);     push_up(ch[root][1]);      push_up(root);}void REVOLVE(int l,int r,int T) //循环右移{    int len=r-l+1;      // len:长度    T=(T%len+len)%len;  //     if(T==0)return;    int c=r-T+1;//将区间[c,r]放在[l,c-1]前面    Splay(get_kth(root,c),0); //将root    Splay(get_kth(root,r+2),root);    int tmp=key_value;    key_value=0;    push_up(ch[root][1]);    push_up(root);    Splay(get_kth(root,l),0);    Splay(get_kth(root,l+1),root);    key_value=tmp;    pre[key_value]=ch[root][1];//这个不用忘记    push_up(ch[root][1]);    push_up(root);}void insert(int x,int p){    Splay(get_kth(root,x+1),0);  // x转至根    Splay(get_kth(root,x+2),root); //x+1转到root右儿子,此时x+2.... 都在右边    newnode(key_value,ch[root][1],p);    push_up(ch[root][1]);    push_up(root);}void del(int x){    Splay(get_kth(root,x),0);  //x-1 移到0     Splay(get_kth(root,x+2),root); //x+1 移到x-1的右边 那么key_value=x    pre[key_value]=0;    key_value=0;   //不知道算不算清除  //    push_up(ch[root][1]);    push_up(root);}int qur_min(int l,int r){     Splay(get_kth(root,l),0);     Splay(get_kth(root,r+2),root);     return m[key_value];}int main() {   // freopen("1.txt","r",stdin);    while (~scanf("%d",&n)) {          for(int i=1;i<=n;i++)              scanf("%d",&a[i]);          init();//这个不能忘记          int q;          scanf("%d",&q);          char op[10];          while(q--){              scanf("%s",op);              if(op[0]=='A'){                  int l,r,ad;                  scanf("%d %d %d",&l,&r,&ad);                  ADD(l,r,ad);              }              else if(op[0]=='I'){                  int x,p;                  scanf("%d %d",&x,&p);                  insert(x,p);              }              else if(op[0]=='D'){                  int x;                  scanf("%d",&x);                  del(x);              }              else if(op[0]=='M'){                  int l,r;                  scanf("%d %d",&l,&r);                  printf("%d\n",qur_min(l,r));              }              else if(op[3]=='E'){  //REVERSE 反转                  int l,r;                  scanf("%d %d",&l,&r);                  REVERSE(l,r);              }              else{     // REVOLVE                  int l,r,k;                  scanf("%d %d %d",&l,&r,&k);                  REVOLVE(l,r,k);              }          }    }    return 0;}

“`

0 0