splay

题目

bzoj3224

代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;
#define N 100005

int n,root,cnt,ch[N][2];//splay本质为二叉排序树
int fa[N],sz[N],w[N],num[N];//num记录出现次数 
bool son(int x) {return ch[fa[x]][1]==x;}//判断是否为右儿子
void link(int x,int y,int f)//将y赋为x的f儿子
{
    //回看rotate,y要放入位置原先可能本来就没有节点,在此处特判
    if(x) ch[x][f]=y;
    if(y) fa[y]=x;
}
void pushup(int x){sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+num[x];}
void rotate(int x,int &rt)//将x转至rt
{
    int y=fa[x],z=fa[y],f=son(x);
    link(z,x,son(y));//x y相对z的大小关系是相同的,将x置于y的位置
    link(y,ch[x][f^1],f);//先看下一行,先将y要放的位置上原来的点接在y下方
    link(x,y,f^1);//若x>y(f=1),则y<x(f=0),^,反之同理
    //注意连接顺序,避免覆盖
    if(y==rt) rt=x;
    pushup(y);pushup(x);//y此时为x的子树,先更新
}
void splay(int x,int &rt)
{
    while(x!=rt)//在x未转至rt时持续上移
    {
        int y=fa[x];
        if(y!=rt) rotate(son(x)^son(y)? x:y,rt);//??? 
        rotate(x,rt);
    }
}
void insert(int &rt,int x,int last)//当前节点,需插入的值,父亲
{
    if(!rt)
    {
        rt=++cnt;//加入新节点,需要取址
        w[rt]=x;fa[rt]=last;sz[rt]=num[rt]=1;
        splay(rt,root);//cxy:没事多转转 
        return;
    }
    sz[rt]++;
    if(x==w[rt]) {num[rt]++;return;}
    insert(ch[rt][w[rt]<x? 1:0],x,rt);
}
int findv(int rt,int x)//返回权值为v的节点第几小 
{
    if(w[rt]==x) return sz[ch[rt][0]]+1;
    if(w[rt]>x) return findv(ch[rt][0],x);
    return sz[ch[rt][0]]+num[rt]+findv(ch[rt][1],x);
}
int findk(int rt,int k)//返回第k大的节点下标
{
    if(sz[ch[rt][0]]<k&&k<=sz[ch[rt][0]]+num[rt]) return rt;
    if(sz[ch[rt][0]]>=k) return findk(ch[rt][0],k);
    return findk(ch[rt][1],k-sz[ch[rt][0]]-num[rt]);
}
void del(int x)//删除权值为v的节点
{
    int k=findv(root,x),t=findk(root,k);
    splay(t,root);//将节点转至根 
    if(num[t]>1) {sz[t]--;num[t]--;return;}//若有多个直接删除 
    //这两句换一下就会挂,是因为转的次数比较少吗??? 
    if(k==sz[root])//最大
    {
        root=ch[t][0];//直接换根
        ch[t][0]=fa[t]=0;//清空
        return; 
    }
    if(k==1)//最小
    {
        root=ch[t][1];
        ch[t][1]=fa[t]=0;
        return;
    }
    int l=findk(root,k-1),r=findk(root,k+1);//前驱后继
    splay(l,root);splay(r,ch[root][1]);//x必处于ch[r][0]
    ch[r][0]=0;
    pushup(r);pushup(l);
    return;
}
int lower(int rt,int x)//查询v的前驱
{
    if(!rt) return -1;//查询到空节点,返回-1
    if(w[rt]>=x) return lower(ch[rt][0],x);
    int t=lower(ch[rt][1],x);
    return t==-1? rt:t;
}
int upper(int rt,int x)//查询v的后继
{
    if(!rt) return -1;
    if(w[rt]<=x) return upper(ch[rt][1],x);
    int t=upper(ch[rt][0],x);
    return t==-1? rt:t;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int opt,x;scanf("%d%d",&opt,&x);
        if(opt==1) insert(root,x,0);
        if(opt==2) del(x);
        if(opt==3) printf("%d\n",findv(root,x));
        if(opt==4) printf("%d\n",w[findk(root,x)]);
        if(opt==5) printf("%d\n",w[lower(root,x)]);
        if(opt==6) printf("%d\n",w[upper(root,x)]);
    }
    return 0;
}
posted @ 2018-01-06 15:15  XYZinc  阅读(257)  评论(0编辑  收藏  举报