@Cwen-oier
2018-07-30T19:39:58.000000Z
字数 2866
阅读 1084
平衡树
参考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,不在就没关系可以转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;
}