HDU 3487 Splay

这道题,首先给你一个长度为n的数组,数组的元素为1 - n ,然后给你m次操作。

一共有两种操作,一种是CUT a, b, c 。这种操作就是先截取a - b位置的元素,然后再将他接到位置c的后面。当然这个位置c是截取掉之后的位置。

第二中操作,FLIP a ,b ,就是将a - b 位置的元素反转。

最后输出m次操作之后数组的元素。

这道题可以用Splay tree来写。


对于操作1,我们要对区间[a , b ]进行操作,首先,找出a - 1和b + 1在树中的位置。使用函数get_Kth(int k )

int get_K(int r , int k){//找第K个值
    push_down(r) ;
    int t = num[ch[r][0]] ;
    if(k == t + 1)return r ;
    if(k <= t)return get_K(ch[r][0] , k) ;
    else return get_K(ch[r][1] , k - t - 1) ;
}


找到a - 1 , b + 1 的位置之后,我们先把a - 1移到树的根节点,然后把b + 1移到根节点的右子树,使用Splay操作。

void Splay(int x ,int s ){//将节点x转到节点s下面
    push_down(x) ;
    while(fa[x] != s){
        if(fa[fa[x]] == s)rotate(x ,ch[fa[x]][0] == x) ;
        else {
            int y = fa[x] ;
            int z = fa[y] ;
            int kind = ( ch[z][0] == y );
            if(ch[y][kind] == x){
                rotate(x ,!kind) ;
                rotate(x , kind) ;
            }
            else {
                rotate(y , kind ) ;
                rotate(x , kind ) ;
            }
        }
    }
    push_up(x) ;
    if(s == 0)root = x ;
}
这样操作之后根节点就是a - 1 ,根节点的右子树就是b + 1。这样操作之后,区间[a , b]就到了根节点右子树的左子树上。



这样,我们就可以对区间[a , b 进行删除了,只需将根节点的右子树的左子树设为0即可。

对于将这个区间插入到c节点后面。先找到c节点的位置,然后将c节点移到根节点,然后找到根节点右子树的最小值,将他移到根节点的右子树,然后将区间 [ a , b ] 接到根节点右子树的左子树上,那么 [ a , b ] 区间就在节点c之后了 。这个操作就完成了。


对于操作2,其实更简单,只需要反转区间就可以。我们可以采用上述同样的操作,将a - 1 移到根节点,将b + 1移到根节点的右子树,那么区间[a , b ]就到了根节点右子树的左子树上,我们对这个节点的FLIP进行取异或即可。


CODE:

作为自己的Splay tree 模版,参考了别人的代码。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

#define inf 2000000000
#define PI acos(-1.0)
#define Max 1111
#define REP(i,s,t) for (int i = s ;i <= t ; ++ i)
#define PII pair<int,int>
#define clr(a,b) memset(a,b,sizeof(a)) ;
using namespace std;

inline void RD(int &ret) {
    char a ;
    do {
        a = getchar() ;
    } while(a < '0' || a > '9') ;
    ret = a - '0' ;
    while((a = getchar()) && a >= '0' && a <= '9') {
        ret = ret * 10 + a - '0' ;
    }
}

int ch[1111111][2] , fa[1111111] , data[1111111] , flip[1111111] ;
int num[1111111] ;
int root , tot ;
int cnt , n ;

void newnode(int &x ,int k ,int father){
    x = ++ tot ;
    ch[x][0] = ch[x][1] = 0 ;
    //num[x] = 1 ;
    data[x] = k ;
    fa[x] = father ;
    flip[x] = 0 ;
}
void push_up(int x){
    num[x] = num[ch[x][0]] + num[ch[x][1]] + 1 ;
}

void push_down(int x ){
    if(flip[x]){
        flip[x] = 0 ;
        flip[ch[x][0]] ^= 1 ;
        flip[ch[x][1]] ^= 1 ;
        swap(ch[x][0] ,ch[x][1]) ;
    }
}
void build(int &s ,int l ,int r ,int father){
    if(l > r)return ;
    int mid = l + r >> 1 ;
    newnode(s ,mid ,father) ;
    build(ch[s][0] , l , mid - 1,s) ;
    build(ch[s][1] , mid + 1 ,r ,s) ;
    push_up(s) ;
}

void init(){
    root = tot = 0 ;
    ch[0][0] = ch[0][1] = fa[0] = num[0] = flip[0] = 0 ;
    newnode(root, -1 , 0 ) ;
    newnode(ch[root][1] ,-1 ,root) ;
    num[root] = 2 ;
    build(ch[ch[root][1]][0] ,1 ,n ,ch[root][1]) ;
    push_up(ch[root][1]) ;
    push_up(root) ;
}

void rotate(int x ,int kind){//kind = 0 ,左旋,kind = 1 ,右旋
    int y = fa[x] ;
    push_down(y) ;
    push_down(x) ;

    ch[y][!kind] = ch[x][kind] ;
    fa[ch[x][kind]] = y ;
    if(fa[y])ch[fa[y]][ch[fa[y]][1] == y] = x ;
    fa[x] = fa[y] ;
    ch[x][kind] = y ;
    fa[y] = x ;
    push_up(y) ;
}

void Splay(int x ,int s ){//将节点x转到节点s下面
    push_down(x) ;
    while(fa[x] != s){
        if(fa[fa[x]] == s)rotate(x ,ch[fa[x]][0] == x) ;
        else {
            int y = fa[x] ;
            int z = fa[y] ;
            int kind = ( ch[z][0] == y );
            if(ch[y][kind] == x){
                rotate(x ,!kind) ;
                rotate(x , kind) ;
            }
            else {
                rotate(y , kind ) ;
                rotate(x , kind ) ;
            }
        }
    }
    push_up(x) ;
    if(s == 0)root = x ;
}
int get_K(int r , int k){//找第K个值
    push_down(r) ;
    int t = num[ch[r][0]] ;
    if(k == t + 1)return r ;
    if(k <= t)return get_K(ch[r][0] , k) ;
    else return get_K(ch[r][1] , k - t - 1) ;
}

int get_MIN(int r ){//找最小值。
    push_down(r) ;
    //int x = ch[r][0] ;
    while(ch[r][0]) {
        r = ch[r][0] ;
        push_down(r) ;
    }
    return r ;
}

int get_MAX(int r){//找最大值。
    push_down(r) ;
    //int x = ch[r][1] ;
    while(ch[r][1]){
        r = ch[r][1] ;
        push_down(r) ;
    }
    return r ;
}

int get_pree(int k){//找前驱。本题没有用到
  //  push_down(k) ;
    int now = ch[k][0] ;
    while(ch[now][1]){
        now = ch[now][1] ;
  //      push_down(now) ;
    }
    return now ;
}

int get_next(int k){//找后继。
    int now = ch[k][1] ;
    while(ch[now][0]){
        now = ch[now][0] ;
    }
    return now ;
}
void rev(int a ,int b){
    int x = get_K(root ,a) ;
    int y = get_K(root ,b + 2) ;
    Splay(x ,0) ;//将x移到根节点。
    Splay(y ,root) ;//将y移到根节点下面。成为x的右子树。
    flip[ch[ch[root][1]][0]] ^= 1 ;//根节点的右子树的左子树,就是区间a , b 将这个区间的flip取异或。即反转操作。
}
void cut(int a ,int b ,int c){
    int x = get_K(root ,a ) ;
    int y = get_K(root ,b + 2) ;
    Splay(x ,0) ;//将x移到根节点
    Splay(y ,root) ;//将y移到根节点下面,成为x的右子树。

    int temp = ch[ch[root][1]][0] ;
    ch[ch[root][1]][0] = 0 ;//删除a - b 区间

    push_up(ch[root][1]) ;
    push_up(root) ;

    int z = get_K(root , c + 1) ;
    //int z = get_next(root) ;
    Splay(z ,0) ; //将c移到根节点。

    int m = get_MIN(ch[root][1] ) ;//根节点右子树的最小值。
    //int m = get_next(root) ;
    Splay( m ,root ) ;//将m移到根节点下。

    ch[ch[root][1]][0] = temp ;//将a - b 区间接上。
    fa[ch[ch[root][1]][0]] = ch[root][1] ;

    push_up(ch[root][1]) ;
    push_up(root) ;
}
void order(int x){
    if(!x)return ;
    push_down(x) ;
    order(ch[x][0]) ;
    if(cnt >= 1 && cnt <= n){
        if(cnt > 1)cout <<' ' ;
        printf("%d",data[x]) ;
    }
    cnt ++ ;
    order(ch[x][1]) ;
}

char str[1111] ;
int main() {
    int op ;
    while(cin >> n >> op){
        if( n == -1 && op == -1)break ;
        init() ;
        REP(i, 0, op - 1){
            cin >> str ;
            if(str[0] == 'C'){
                int a , b , c ;
                RD(a) ;RD(b) ;RD(c) ;
                cut(a, b , c ) ;
            }else {
                int a ,b ;
                RD(a) ;RD(b) ;
                rev(a , b) ;
            }
        }
        cnt = 0 ;
        order(root) ;
        cout << endl;
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值