splay 不停旋转保持树高,比treap还是要快的
#define rg register #define _ (int)1e5+1 using namespace std; int n,m,root,num; struct pp { int son[2],dad,it,cnt,size; //cnt:这个点有多少个值;size:子树上有多少个值 }tr[_];//好像空间是O(n)吧? inline int read() { rg int save=0,w=1;rg char q=getchar(); //!!!!!! =getsonar() 不可少!!!! (否则q可能会有初值) while(q<'0'||q>'9'){if(q=='-')w=-1;q=getchar();} while(q>='0'&&q<='9')save=(save<<3)+(save<<1)+q-'0',q=getchar(); return w*save; } inline void pushup(rg int x) { tr[x].size=tr[tr[x].son[0]].size+tr[tr[x].son[1]].size+tr[x].cnt;//加了两个儿子别忘了加自己 } inline void doit(rg int x)//一个结点的旋转 { rg int y=tr[x].dad,z=tr[y].dad; rg bool k=tr[y].son[1]==x; tr[x].dad=z; tr[z].son[tr[z].son[1]==y]=x; tr[y].son[k]=tr[x].son[!k]; tr[tr[y].son[k]].dad=y; tr[y].dad=x; tr[x].son[!k]=y; pushup(y),pushup(x);//记住要更新size!!! } inline void splay(rg int x,rg int to)//把某节点旋转多次(或一次)成to的儿子 { while(tr[x].dad!=to) { rg int y=tr[x].dad,z=tr[y].dad; //由于x与y在其父亲的同側时,如果只是一路把x转上去的话,有一条链始终没被拆,保证不了时间复杂度,所以我们这样做: if(z!=to) // ((tr[z].son[1]==y)^(tr[y].son[1]==x))?doit(x):doit(y);//在其父亲同側就转y,不在就没关系可以转x doit(x); } if(!to)root=x; //记得更新根节点!!! } inline void find(rg int x)//值为x的点 { rg int now=root; if(!now)return;//看情况加不加此句 while(tr[now].it!=x&&tr[now].son[x>tr[now].it]) now=tr[now].son[x>tr[now].it]; splay(now,0); } inline void insert(rg int x) { rg int now=root,fa=0; while(now&&tr[now].it!=x) //先从根节点往下找到第一个 值与x相等 或 不存在 的点 fa=now,now=tr[now].son[x>tr[now].it]; //1: right son, 2: left son if(tr[now].it==x) tr[now].cnt++; // else { now=++num; tr[now].it=x,tr[now].cnt=1; // tr[now].dad=fa; tr[now].size=1; if(fa) tr[fa].son[x>tr[fa].it]=now; // tr[now].size++; //attention!!!反正在下一行的splay()中会进入pushup()函数而计算子树大小,所以无需提前算size } splay(now,0); } inline int Kth(rg int x) { rg int now=root; if(x>tr[root].size)return 0; while(666) { rg int front=tr[tr[now].son[0]].size+tr[now].cnt; if(x<=front&&x>tr[tr[now].son[0]].size)return tr[now].it; if(x<=tr[tr[now].son[0]].size) now=tr[now].son[0]; else now=tr[now].son[1],x-=front; /* if(x>front)now=tr[now].son[1],x-=front; else return tr[now].it; */ } } inline int next(rg int x,rg bool b) { find(x); rg int now=root; if(x<tr[now].it&&b)return now;//返回的是指针啊喂 if(x>tr[now].it&&(!b))return now; now=tr[now].son[b]; while(tr[now].son[!b])now=tr[now].son[!b]; return now; } inline void cut(rg int x) { rg int l=next(x,0); rg int r=next(x,1); splay(l,0); splay(r,l); rg int now=tr[r].son[0]; if(tr[now].cnt>1) { tr[now].cnt--; splay(now,0); } else { if(tr[r].son[0]==num)--num; tr[r].son[0]=0; // pushup(r),pushup(root); //不用上上行的splay操作,但记得要pushup,不然假如下一个操作是求第k大,size没更新就可能错啦 //但其实也不用pushup(你已经把前驱转到了根节点,这个点在后继的左儿子,求第k大的时候会考虑到它的size) } } int main() { n=read(); rg int i,j; // for(i=1;i<=n;++i)tr[i].it=i,tr[i].son[1]=i+1,tr[i].dad=i-1;错误建树示范(必须不停旋转让树保持log(n)的树高,这样建条链不行) insert(2147483647),insert(-2147483647);//给树最两端的点以前驱和后继,方便删除它们 for(i=1;i<=n;++i) { rg int op=read(),x=read(); if(op==1) insert(x); if(op==2) cut(x); if(op==3) find(x),printf("%d\n",tr[tr[root].son[0]].size); if(op==4) printf("%d\n",Kth(x+1)); if(op==5) printf("%d\n",tr[next(x,0)].it); if(op==6) printf("%d\n",tr[next(x,1)].it); } return 0; }