@Cwen-oier
2018-07-30T11:39:58.000000Z
字数 2866
阅读 1219
平衡树
参考yyb博客(yyb博客里没管第k大(故未记size),以下代码可供参考)
my 板子:(洛谷【模板】普通平衡树(Splay))
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,不在就没关系可以转xdoit(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 sonif(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;}