splay
题目
bzoj3224
代码
#include
#include
#include
#include
#include
#include
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),则yx) 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) 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;
}