SGU 187 Twist and whirl

  伸展树。。。

  赤果果地抄袭雷哥的代码:http://www.cnblogs.com/jianglangcaijin/archive/2013/01/21/2869148.html

  题意:输入n,m。给你n个数,初始排列是1到n,然后有m个操作,每次操作给两个数L,R,把(L,R)区间上的数的顺序颠倒,输出最终的顺序。

  伸展树就是二叉查找排序数,一个节点的左子树上的数都比它小,右子树上的数比它大。这道题要在首尾加上哨兵,即有n+2个数,操作(L,R),就是把L-1旋转至根节点,然后把R+1旋转至L-1的右子树,那么R+1的左子树就是区间(L,R),然后把这个区间颠倒。

  因为是第一次看伸展树的代码,雷哥定义的数组看了好大一会儿才知道干吗用的。

  c[f][0]是节点f的左子树,c[f][1]就是节点f的右子树。p[x]是节点x的父节点,s[x]是以节点x为跟的数的节点数,sign[x]表示节点x是否颠倒,和线段树有点像。

下面就贴代码了,函数的功能也在代码里注释。

#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;

const int N=130005;
int n,m;
int root;
int sign[N],c[N][2],s[N],p[N];
void down(int x)          //将以节点x为根的树颠倒
{
    if(!sign[x])    return ;
    sign[x]=0;
    swap(c[x][0],c[x][1]);
    sign[c[x][0]]^=1;
    sign[c[x][1]]^=1;
}
void update(int f,int x,int flag)//建立节点x,使它的父节点为f,flag表示它是左子树还是右子树
{
    p[x]=f;
    if(!f)  return ;
    c[f][flag]=x;
    s[f]=1+s[c[f][0]]+s[c[f][1]];
}
int build(int L,int R)       //建树
{
    if(L>R) return 0;
    int m=(L+R)>>1;
    update(m,build(L,m-1),0);
    update(m,build(m+1,R),1);
    return m;
}
void rotate(int x)         //旋转节点x
{
    int P=p[x],G=p[P];
    if(c[P][0]==x)//如果x是P的左子树
    {
        update(P,c[x][1],0);//x的右子树转为P的左子树
        update(x,P,1);//P成为x的右子树
        update(G,x,!(c[G][0]==P));
    }
    else
    {
        update(P,c[x][0],1);
        update(x,P,0);
        update(G,x,!(c[G][0]==P));
    }
}
void splay(int x,int &goal)
{
    int P=p[x],G=p[P],limit=p[goal];
    while(P!=limit)
    {
        if(G!=limit && (c[G][0]==P)==(c[P][0]==x))  rotate(P);
        rotate(x);
        P=p[x];
        G=p[P];
    }
    goal=x;
    down(goal);
}
int select(int x)
{
    int t=root;
    while(1)
    {
        down(t);
        if(s[c[t][0]]+1==x)    break;
        if(s[c[t][0]]+1>x)     t=c[t][0];
        else            x-=s[c[t][0]]+1,t=c[t][1];
    }
    return t;
}
int main()
{
    scanf("%d%d",&n,&m);
    root=build(1,n+2);
    int l,r;
    while(m--)
    {
        scanf("%d%d",&l,&r);
        int r1=select(l);
        int r2=select(r+2);
        splay(r1,root);
        splay(r2,c[r1][1]);
        int x=c[root][1];
        x=c[x][0];
        sign[x]^=1;
    }
    for(int i=2;i<=n+1;i++)  printf("%d ",select(i)-1);
    puts("");
    return 0;
}
原文地址:https://www.cnblogs.com/yongren1zu/p/3286192.html