P3369 【模板】普通平衡树 题解(Splay)

题目链接

P3369 【模板】普通平衡树

解题思路

注意查询的时候大于小于等于号千万不要搞错了;注意适时伸展

AC代码

#include<stdio.h>
#define root t[0].s[1]
struct Tree{
    int s[2];//son
    int sum;//总 数字 数 
    int cnt;//frequence
    int f;//father
    int data;
}t[200010];
int size,tot;//最大节点序号,总 数字 数 
void upd(int x){
    t[x].sum=t[t[x].s[0]].sum+t[t[x].s[1]].sum+t[x].cnt;//更新节点 
}
int id(int x){//查询该节点为左儿子还是右儿子 
    return x==t[t[x].f].s[0]?0:1;
}
void connect(int x,int fa,int son){//x->fa,fa.son->x 
    t[x].f=fa;
    t[fa].s[son]=x;
}
void rotate(int x){//x为中心的旋转 
    int y=t[x].f,idx=id(x),idy=id(y),B=t[x].s[idx^1],R=t[y].f;
    connect(B,y,idx);
    connect(y,x,idx^1);
    connect(x,R,idy);
    upd(x);upd(y);
}
void splay(int now,int to){//把now转为to的父亲的孩子节点(转到to的位置) 
    to=t[to].f;
    while(to!=t[now].f){
        int up=t[now].f;
        if(to==t[up].f)rotate(now);//只能翻上去一次 
        else if(id(now)==id(up)){
            rotate(up);
            rotate(now);
        }
        else{
            rotate(now);
            rotate(now);
        }
    }
}
int create(int v,int fa){//建立节点 
    t[++size].data=v;
    t[size].f=fa;
    t[size].cnt=t[size].sum=1;
    return size;
}
void push(int v){//找到插入的位置插入 
    int sign=0,nxt;
    tot++;
    if(!root)root=create(v,0);
    else{
        int now=root;
        while(now){
            t[now].sum++;
            if(t[now].data==v){
                t[now].cnt++,sign=now;
                break;
            }
            nxt=t[now].data<v?1:0;
            if(!t[now].s[nxt]){
                sign=t[now].s[nxt]=create(v,now);
                break;
            }
            now=t[now].s[nxt];
        }
    }
    splay(sign,root);
}
int find(int v){
    int now=root,nxt;
    while(now){
        if(t[now].data==v){
            splay(now,root);
            return now;
        }
        nxt=t[now].data<v?1:0;
        now=t[now].s[nxt];
    }
    return 0;
}
void destroy(int x){//删除节点 
    t[x].cnt=t[x].data=t[x].f=t[x].sum=t[x].s[0]=t[x].s[1]=0;
    if(x==size)size--;
}
void pop(int v){
    int node=find(v);
    if(!node)return;
    tot--;
    if(t[node].cnt>1){
        t[node].cnt--,t[node].sum--;
        return;
    }
    if(!t[node].s[0])root=t[node].s[1],t[root].f=0;
    else{
        int mx=t[node].s[0],B=t[node].s[1];
        while(t[mx].s[1])mx=t[mx].s[1];//找到左子树最大点,翻上来 
        splay(mx,root);
        connect(B,mx,1);
        connect(mx,0,1);
        upd(mx);
    }
    destroy(node);
}
int rank(int v){
    int ans=0,now=root,nxt;
    while(now){
        if(t[now].data==v){
            int r=ans+t[t[now].s[0]].sum+1;
            splay(now,root);
            return r;
        }
        nxt=t[now].data<v?1:0;
        if(nxt)ans+=t[t[now].s[0]].sum+t[now].cnt;
        now=t[now].s[nxt];
    }
    return ans;
}
int value(int x){
    int now=root,les,res;
    while(1){
        les=t[t[now].s[0]].sum;
        res=les+t[now].cnt;
        if(res<x)now=t[now].s[1],x-=res;
        else if(les>=x)now=t[now].s[0];
        else{splay(now,root);return t[now].data;}
    }
}
int upper(int v){
    int now=root,res=1e9;
    while(now){
        if(t[now].data>v&&t[now].data<res)res=t[now].data;
        now=t[now].s[t[now].data>v?0:1];
    }
    return res;
}
int lower(int v){
    int now=root,res=-1e9;
    while(now){
        if(t[now].data<v&&t[now].data>res)res=t[now].data;
        now=t[now].s[t[now].data>=v?0:1];//注意大于等于 
    }
    return res;
}
int main(){
    int i,n,opt,num;
    scanf("%d",&n);
    for(i=0;i<n;i++){
        scanf("%d%d",&opt,&num);
        if(opt==1)push(num);
        else if(opt==2)pop(num);
        else if(opt==3)printf("%d\n",rank(num));
        else if(opt==4)printf("%d\n",value(num));
        else if(opt==5)printf("%d\n",lower(num));
        else printf("%d\n",upper(num));
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Potassium/p/10331396.html
今日推荐