P3369 【模板】普通平衡树
splay模板
我只是发一个模板...
我太弱了讲不清
注意操作时可能会访问到最小值的前驱或最大值的后继
所以要多加入两个虚节点INF和 -INF防止越界
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int N=1e5+7; inline int read() { register int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int n; //------------------------------Splay-------------------------------- int ch[N<<2][2],sz[N<<2],fa[N<<2],val[N<<2],num[N<<2],cnt,rt; inline void pushup(int x){ sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+num[x]; }//更新当前节点 inline void rotate(int x,int &k)//伸展 { int y=fa[x],z=fa[y],d=(ch[y][1]==x); if(y==k) k=x; else ch[z][(ch[z][1]==y)]=x; fa[x]=z; fa[y]=x; fa[ch[x][d^1]]=y; ch[y][d]=ch[x][d^1]; ch[x][d^1]=y; pushup(y); pushup(x); } inline void splay(int x,int &k)//splay { while(x!=k) { int y=fa[x],z=fa[y]; if(y!=k) { if( (ch[y][1]==x)^(ch[z][1]==y) ) rotate(x,k); else rotate(y,k); } rotate(x,k); } } inline void find(int x)//找到数x,并将它弄到树根 { int now=rt; while(ch[now][ x>val[now] ] && x!=val[now]) now=ch[now][ x>val[now] ]; splay(now,rt); } inline int Q_kth(int k)//询问第k大 { int now=rt; while(233) { if(ch[now][0]&&k<=sz[ch[now][0]]) { now=ch[now][0]; continue; } if(k>sz[ ch[now][0] ]+num[now] ) { k-=sz[ ch[now][0] ]+num[now]; now=ch[now][1]; continue; } return now; } } inline void Q_rank(int x)//询问x的排名 { find(x);//把它转到树根,然后输出左子树的大小就好了(左边都比它小,右边都比它大) printf("%d\n",sz[ch[rt][0]]/*注意不用加1,因为有多一个虚节点-INF在左边*/); } inline int pre(int x)//询问前驱,把节点转到根然后输出左子树中的最大值(即左子树最右边的节点) { find(x); if(val[rt]<x) return rt;//特判一下 int now=ch[rt][0];//左子树 while(ch[now][1]) now=ch[now][1];//一直往右走 return now; } inline int nex(int x)//同理 { find(x); if(val[rt]>x) return rt; int now=ch[rt][1]; while(ch[now][0]) now=ch[now][0]; return now; } inline void ins(int x)//插入节点 { int now=rt,f=0; while(now&&val[now]!=x)//先找到位置 { f=now; now=ch[now][ x>val[now] ]; } if(now) num[now]++,sz[now]++;//如果已经有值就直接更新 else//否则就增加节点 { now=++cnt; if(f) ch[f][ x>val[f] ]=now; val[now]=x; fa[now]=f; num[now]=sz[now]=1; } splay(now,rt);//最后要Splay一波来更新整颗树的状态 } inline void erase(int x)//删除节点,把前驱转到根,后继转到根的右儿子,那么后继的左子树有且只有当前节点 { int now=nex(x); splay(pre(x),rt); splay(now,ch[rt][1]); now=ch[now][0]; if(num[now]>1)//如果不止一个就减1 num[now]--,sz[now]--; else ch[fa[now]][0]=0;//否则就删除节点 pushup(fa[now]); pushup(fa[fa[now]]); } //------------------------------------------------------------ int main() { n=read(); ins(0x3f3f3f3f); ins(0xcfcfcfcf);//插入虚节点 int a,b; for(int i=1;i<=n;i++) { a=read(); b=read(); if(a==1) ins(b); if(a==2) erase(b); if(a==3) Q_rank(b); if(a==4) printf("%d\n",val[ Q_kth(b+1/*注意+1,因为有虚节点*/) ]); if(a==5) printf("%d\n",val[ pre(b) ]); if(a==6) printf("%d\n",val[ nex(b) ]); } return 0; }