[lydsy] 3224 普通平衡树 [Splay]

问题描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个)
  3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数)
  6. 求x的后继(后继定义为大于x,且最小的数)

输入格式:
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

输出格式:
对于操作3,4,5,6每行输出一个数,表示对应答案

题目来源 http://www.lydsy.com/JudgeOnline/problem.php?id=3224

splay笔记 》》http://blog.csdn.net/ctsas/article/details/70524330
(将理论实现真的好不容易,这代码耗费了我好几天时间 QAQ)

#include<cstdio>
#include<cstring>
using namespace std;

struct node{
    node *left,*right;
    node *parent;
    int key;
    int size;   //子树大小
    int cnt;    //该节点key重数
    node(){
        left=right=parent=NULL;
        size=cnt=0;
    }
}*root=NULL;

void update(node *x){
    if(!x) return ;
    x->size=x->cnt;
    if(x->left) x->size+=x->left->size;
    if(x->right)x->size+=x->right->size;
}

void zig(node *x){
    node *y=x->parent;
    if(y->parent){
        if(y->parent->left==y)
            y->parent->left=x;
        else
            y->parent->right=x;
    }
    x->parent=y->parent;
    y->parent=x;
    y->left=x->right; if(y->left) y->left->parent=y;
    x->right=y;
    if(!x->parent) root=x;

    update(y);update(x);
}

void zag(node *x){
    node *y=x->parent;
    if(y->parent){
        if(y->parent->left==y)
            y->parent->left=x;
        else
            y->parent->right=x;
    }
    x->parent=y->parent;
    y->parent=x;
    y->right=x->left; if(y->right) y->right->parent=y;
    x->left=y;
    if(!x->parent) root=x;

    update(y);update(x);
}

void splay(node *x){
    while(x&&x->parent){
        if(!x->parent->parent){
            if(x->parent->left==x)
                zig(x);
            else
                zag(x);
        }
        else if(x->parent->left==x&&x->parent->parent->left==x->parent){
            zig(x->parent);//zig(x);注意啊 写成zig(x)就失去splay的意义了
            zig(x);
        }
        else if(x->parent->right==x&&x->parent->parent->right==x->parent){
            zag(x->parent);//zag(x);
            zag(x);
        }
        else if(x->parent->left==x&&x->parent->parent->right==x->parent){
            zig(x);
            zag(x);
        }
        else{
            zag(x);
            zig(x);
        }
    }
}

node* subtree_minimum(node *u){
    while(u->left) u=u->left;
    return u;
}

node* subtree_maximum(node *u){
    while(u->right) u=u->right;
    return u;
}

bool cmop(int x,int y){
    return x<y;
}

node *find(int key){
    node *z=root;
    while(z){
        if(cmop(z->key , key)) z=z->right;
        else if(cmop(key , z->key)) z=z->left;
        else {
            splay(z);
            return z;
        }
    }
    return NULL;
}

int Rank(int key){
    int ans=0;
    node *z=root;
    while(z){
        if(key<z->key) z=z->left;
        else{
            ans+=z->left?z->left->size:0;
            if(key==z->key){
                splay(z);
                return ans+1;
            }
            ans+=z->cnt;
            z=z->right;
        }
    }
    return ans+1;
}

node *select(int R){
    node *z=root,*p;
    while(z){
        p=z;
        if(z->left&&R<=z->left->size) z=z->left;
        else{
            int r=(z->left?z->left->size:0)+z->cnt;
            if(R<=r) break;
            R-=r;
            z=z->right;
        }
    }
    splay(p);
    return p;
}

void insert(int key){
    node *z=root;
    node *p=find(key);
    if(p){
        p->cnt++;
        p->size++;
        return ;
    }

    while(z){
        p=z;
        if(cmop(z->key , key)) z=z->right;
        else z=z->left;
    }
    z=new node;
    z->key=key;
    z->parent=p;
    z->size=1;
    z->cnt=1;

    if(!p) root=z;
    else {
        if(cmop(p->key , z->key)) p->right=z;
        else p->left=z;
        update(p);
        splay(z);
    }
}

//下面两个 pre 和 suc 貌似可以利用搜索顺序进行逻辑优化
node *pre(int key){
    node *z=root,*p=NULL;
    while(z){
        if(key>z->key&&(p==NULL||p&&z->key>p->key)) p=z;
        if(key>z->key) z=z->right;
        else z=z->left;
    }
    splay(p);
    return p;
}

node *suc(int key){
    node *z=root,*p=NULL;
    while(z){
        if(key<z->key&&(p==NULL||p&&z->key<p->key)) p=z;
        if(key>=z->key) z=z->right;
        else z=z->left;
    }
    splay(p);
    return p;
}

void erase(int key){
    node *z=find(key);
    if(!z) return ;

    if(z->cnt>1){
        z->cnt--;
        z->size--;
        return ;
    }
    if(!z->left&&!z->right){
        delete z;
        root=NULL;
    }
    else if(!z->left){
        root=z->right;
        z->right->parent=NULL;
        delete z;
        update(root);
    }
    else if(!z->right){
        root=z->left;
        z->left->parent=NULL;
        delete z;
        update(root);
    }
    else {
        node *y=subtree_minimum(z->right);
        z->left->parent=y;
        y->left=z->left;
        root=z->right;
        z->right->parent=NULL;
        delete z;
        update(y);
        update(root);
        splay(y);
    }
}

int main()
{
//  freopen("C:\\Users\\chutzpah\\Desktop\\ot.txt","r",stdin);
//  freopen("C:\\Users\\chutzpah\\Desktop\\2.txt","w",stdout);
    int n;scanf("%d",&n);
    while(n--){
        int op,c,ans,r,l;node *t;
        scanf("%d%d",&op,&c);
//      if(op>2) printf("%d %5d| ",op,c);
        switch(op){
            case 1: insert(c);break;
            case 2: erase(c);break;
            case 3: printf("%d\n",Rank(c));break;
            case 4: printf("%d\n",select(c)->key); break;
            case 5: printf("%d\n",pre(c)->key); break;
            case 6: printf("%d\n",suc(c)->key); break;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值