https://www.luogu.org/problemnew/show/P3369
#include<bits/stdc++.h> using namespace std; inline int read(){ int sum=0,x=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){ if(ch==‘-‘) x=0; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘) sum=(sum<<1)+(sum<<3)+(ch^48),ch=getchar(); return x?sum:-sum; } inline void write(int x){ if(x<0) putchar(‘-‘),x=-x; if(x>9) write(x/10); putchar(x%10+‘0‘); } const int M=2e5+5; const int inf=0x3f3f3f3f; int ch[M][2],val[M],cnt[M],fa[M],sz[M],ncnt,root; int ck(int x){ return ch[fa[x]][1]==x; } void up(int x){ sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x]; } void rotate(int x){ int y=fa[x]; int z=fa[y]; int k=ck(x); int w=ch[x][k^1]; ch[y][k]=w,fa[w]=y; ch[z][ck(y)]=x,fa[x]=z; ch[x][k^1]=y,fa[y]=x; up(y),up(x); } void splay(int x,int goal=0){//将x旋转为goal的儿子,如果goal是0则旋转到根 while(fa[x]!=goal){//一直旋转到x成为goal的儿子 int y=fa[x],z=fa[y];//父节点祖父节点 if(z!=goal)//如果Y不是根节点,则分为两类来旋转(如果随意旋转会达不到查询效果) ck(x)^ck(y)?rotate(x):rotate(y); rotate(x);//无论怎么样最后的一个操作都是旋转x } if(!goal) root=x;//如果goal是0,则将根节点更新为x } void find(int x){//查找x的位置,并将其旋转到根节点 if(!root)//树空 return ; int cur=root; while(ch[cur][x>val[cur]]&&x!=val[cur])//当存在儿子并且当前位置的值不等于x cur=ch[cur][x>val[cur]];//跳转到儿子,查找x的父节点 splay(cur);//把当前位置旋转到根节点 } void insertt(int x){//插入x int cur=root,p=0;//当前位置cur,cur的父节点p while(cur&&val[cur]!=x){//当u存在并且没有移动到当前的值 p=cur;//向下u的儿子,父节点变为u cur=ch[cur][x>val[cur]];//大于当前位置则向右找,否则向左找 } if(cur)//存在这个值的位置 cnt[cur]++;//增加一个数 else{//不存在这个数字,要新建一个节点来存放 cur=++ncnt;//新节点的位置 if(p)//如果父节点非根 ch[p][x>val[p]]=cur;//不存在儿子 fa[cur]=p;//父节点 ch[cur][0]=ch[cur][1]=0; val[cur]=x;//值 cnt[cur]=sz[cur]=1;//数量,大小 } splay(cur);//把当前位置移到根,保证结构的平衡。注意前面因为更改了子树大小,所以这里必须Splay上去进行pushup保证size的正确。 } int kth(int k){ int u=root; while(true){ if(ch[u][0]&&k<=sz[ch[u][0]]) u=ch[u][0]; else if(k>sz[ch[u][0]]+cnt[u]) k-=sz[ch[u][0]]+cnt[u],u=ch[u][1]; else return u; } } int pre(int x){//查找x的前驱(0)或者后继(1) find(x); if(val[root]<x)//如果当前节点的值小于x并且要查找的是前驱 return root; int u=ch[root][0];//前驱再左儿子上找 while(ch[u][1]) u=ch[u][1]; return u;//返回位置 } int succ(int x){ find(x); if(val[root]>x)//如果当前节点的值大于x并且要查找的是后继 return root; int u=ch[root][1];//后继在右儿子上找 while(ch[u][0]) u=ch[u][0]; return u; } void remove(int x){ int las=pre(x),nex=succ(x); splay(las,0),splay(nex,las); //将前驱旋转到根节点,后继旋转到根节点下面 //很明显,此时后继是前驱的右儿子,x是后继的左儿子,并且x是叶子节点 int del=ch[nex][0];//后继的左儿子 if(cnt[del]>1)//如果超过一个 cnt[del]--,splay(del,0);//直接减少一个 else ch[nex][0]=0;//这个节点直接丢掉(不存在了) } void init(){ memset(ch,0,sizeof(cnt)); memset(cnt,0,sizeof(cnt)); memset(sz,0,sizeof(sz)); memset(val,0,sizeof(val)); memset(fa,0,sizeof(fa)); ncnt=root=0; } int main(){ int n; while(~scanf("%d",&n)){ init(); insertt(inf); insertt(-inf); while(n--){ int op=read(),x=read(); if(op==1) insertt(x); else if(op==2) remove(x); else if(op==3) find(x),write(sz[ch[root][0]]),putchar(‘\n‘); else if(op==4) write(val[kth(x+1)]),putchar(‘\n‘); else if(op==5) write(val[pre(x)]),putchar(‘\n‘); else write(val[succ(x)]),putchar(‘\n‘); } putchar(‘\n‘); } return 0; }
原文:https://www.cnblogs.com/starve/p/10883512.html