[AH2017/HNOI2017]单旋

题目链接

https://www.luogu.com.cn/problem/P3721

做法

好神的题目!!!

我们重新修改一下链的定义,一条链为\(a_1,a_2,a_3,a_4,a_5...\),仅当\(a_{i}(∀i>1)\)\(a_{i-1}\)的左儿子,或者右儿子。

不难发现,对于一条链,链底旋到链顶,其实只会把链底和其的某个儿子带到根节点,其余不变。
在这里插入图片描述
就在我思考不是链的情况,膜了一下题解,发现最大最小值到根节点一定是一条链!!!

且其最多只有一个儿子,要么左儿子,要么右儿子,且这两个儿子会在第一次旋转的时候过继给其父亲,并不会跟随其带动根当中。

所以每次\(splay\)只需要用线段树统计深度(怎么维护后面说),用\(rt\)储存根节点,然后记录一下\(fa\)\(son\)数组即可反正每次最多修改几个,暴力修改即可

还需要记住平衡树有个非常妙的性质,就是如果把原数组排序离散化,那么一个点的子树的中序遍历刚好就是这个数组上连续的一段,再看操作,例如:删除掉\(max\)值,其实就是把\(max\)的左儿子(也是其唯一的儿子)的子树深度全部\(-1\),那么对应在排序离散后的数组,其实就是这个子树所对应的连续的一段,直接用线段树维护每个位置的深度即可。

代码如下(貌似我是连\(x\)一同进行深度修改的):

inline  void  del_dian(int  x)//x不能是根节点 
{
	int  f=fa[x],w=son[f][0]==x?0:1;
	int  l=0,r=0;//修改的范围
	if(w==0)l=x,r=f-1;//最小值 
	else  l=f+1,r=x;//最大值 
	change1(1,1,qlen,l,r,-1);//深度减少1 
	fa[x]=0;
	if(son[x][0])fa[son[x][0]]=f,son[f][w]=son[x][0];
	else  fa[son[x][1]]=f,son[f][w]=son[x][1];
}

当然,可能有人会问啦,每次插入一个数字,你怎么用线段树动态维护数组排序和离散化之后的数组呢?其实我们只要把最终的数组排序离散化一下,然后对于没出现过的数字不管他,然后数字出现单点修改即可。(因为代价只用单点查询)

但是插入到底会插入在哪个数字下面呢?

不难发现,要么前驱,要么后继(求前驱后继用set实现),这里又分两种情况了,要么一个在另外一个子树之内,要么在不同的子树,分类讨论即可。(只要运用好“把原数组排序离散化,那么一个点的子树的中序遍历刚好就是这个数组上连续的一段”的性质,这个操作应该不难想)

加入操作:

inline  void  add(int  k,int  father)
{
	fa[k]=father;
	if(father>k)son[father][0]=k;
	else  son[father][1]=k;
}
inline  int  ins(int  x)
{
	fuck.insert(x);++cnt;
	if(!root){change2(1,1,qlen,x,1);root=x;return  1;}
	else
	{
		set<int>::iterator  pre,hou;
		pre=fuck.lower_bound(x);
		hou=fuck.upper_bound(x);
		
		int  deppre=999999999,dephou=999999999;
		if(pre!=fuck.begin())
		{
			pre--;
			deppre=findans(1,1,qlen,*pre);
		}
		if(hou!=fuck.end())dephou=findans(1,1,qlen,*hou);
		int  father;
		if(deppre<dephou)//前面的节点是我的祖先 
		{
			if(!son[*pre][1])add(x,*pre),father=*pre;
			else  add(x,*hou),father=*hou;
		}
		else
		{
			if(!son[*hou][0])add(x,*hou),father=*hou;
			else  add(x,*pre),father=*pre;
		}
		if(father==*hou)
		{
			change2(1,1,qlen,x,dephou+1);
			return  dephou+1;
		}
		else
		{
			change2(1,1,qlen,x,deppre+1);
			return  deppre+1;
		}
	}
}

所以,总的来说,时间复杂度就是\(O(nlogn)\)

#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
#define  N  110000
#define  NN  210000
using  namespace  std;
typedef  long  long  LL;
int  n;
struct  node
{
	int  l,r,lazy;
}tr[NN];int  len,rt;
inline  void  pushlazy(int  x,int  k){tr[x].lazy+=k;}
inline  void  downdata(int  x)
{
	if(tr[x].lazy)
	{
		pushlazy(tr[x].l,tr[x].lazy);pushlazy(tr[x].r,tr[x].lazy);
		tr[x].lazy=0;
	}
}
inline  void  bt(int  l,int  r)
{
	int  now=++len;
	if(l<r)
	{
		int  mid=(l+r)>>1;
		tr[now].l=len+1;bt(l,mid);
		tr[now].r=len+1;bt(mid+1,r);
	}
	else  tr[now].lazy=0;
}
inline  void  change1(int  now,int  l,int  r,int  ll,int  rr,int  k)
{
	if(l==ll  &&  r==rr){pushlazy(now,k);return  ;}
	int  mid=(l+r)>>1;
	downdata(now);
	if(rr<=mid)change1(tr[now].l,l,mid,ll,rr,k);
	else  if(mid<ll)change1(tr[now].r,mid+1,r,ll,rr,k);
	else  change1(tr[now].l,l,mid,ll,mid,k),change1(tr[now].r,mid+1,r,mid+1,rr,k);
}
inline  void  change2(int  now,int  l,int  r,int  k,int  c)
{
	if(l==r){tr[now].lazy=c;return  ;}
	int  mid=(l+r)>>1;
	downdata(now);
	if(k<=mid)change2(tr[now].l,l,mid,k,c);
	else  change2(tr[now].r,mid+1,r,k,c);
}
inline  int  findans(int  now,int  l,int  r,int  k)
{
	if(l==r)return  tr[now].lazy;
	int  mid=(l+r)>>1;
	downdata(now);
	if(k<=mid)return  findans(tr[now].l,l,mid,k);
	else  return  findans(tr[now].r,mid+1,r,k);
}
int  fa[N],son[N][2],root/*根节点*/,cnt,qlen;
set<int> fuck;
inline  void  add(int  k,int  father)
{
	fa[k]=father;
	if(father>k)son[father][0]=k;
	else  son[father][1]=k;
}
inline  int  ins(int  x)
{
	fuck.insert(x);++cnt;
	if(!root){change2(1,1,qlen,x,1);root=x;return  1;}
	else
	{
		set<int>::iterator  pre,hou;
		pre=fuck.lower_bound(x);
		hou=fuck.upper_bound(x);
		
		int  deppre=999999999,dephou=999999999;
		if(pre!=fuck.begin())
		{
			pre--;
			deppre=findans(1,1,qlen,*pre);
		}
		if(hou!=fuck.end())dephou=findans(1,1,qlen,*hou);
		int  father;
		if(deppre<dephou)//前面的节点是我的祖先 
		{
			if(!son[*pre][1])add(x,*pre),father=*pre;
			else  add(x,*hou),father=*hou;
		}
		else
		{
			if(!son[*hou][0])add(x,*hou),father=*hou;
			else  add(x,*pre),father=*pre;
		}
		if(father==*hou)
		{
			change2(1,1,qlen,x,dephou+1);
			return  dephou+1;
		}
		else
		{
			change2(1,1,qlen,x,deppre+1);
			return  deppre+1;
		}
	}
}
inline  void  del_dian(int  x)//x不能是根节点 
{
	int  f=fa[x],w=son[f][0]==x?0:1;
	int  l=0,r=0;//修改的范围
	if(w==0)l=x,r=f-1;//最小值 
	else  l=f+1,r=x;//最大值 
	change1(1,1,qlen,l,r,-1);//深度减少1 
	fa[x]=0;
	if(son[x][0])fa[son[x][0]]=f,son[f][w]=son[x][0];
	else  fa[son[x][1]]=f,son[f][w]=son[x][1];
}
inline  int  del(int  x/*删除数字并且返回代价*/)
{
	fuck.erase(x);
	if(x==root)
	{
		if(son[x][0])root=son[x][0],fa[root]=0,son[x][0]=0;
		else  root=son[x][1],fa[root]=0,son[x][1]=0;
		change1(1,1,qlen,1,qlen,-1);//全部深度减一 
		return  1;
	}
	else
	{
		int  ans=findans(1,1,qlen,x);
		del_dian(x);
		return  ans;
	}
}
int  q[N];int  id[N];
inline  bool  cmp(int  x,int  y){return  q[x]<q[y];}
int  main()
{
//	freopen("1.out","w",stdout);
	scanf("%d",&n);
	for(int  i=1;i<=n;i++)
	{
		int  type;
		scanf("%d",&type);
		if(type!=1)q[i]=-type;
		else
		{
			int  x;scanf("%d",&x);
			q[i]=x;
			qlen++;id[qlen]=i;
		}
	}
	sort(id+1,id+qlen+1,cmp);
	for(int  i=1;i<=qlen;i++)q[id[i]]=i;
	bt(1,qlen);
	int  ans=0;
	for(int  i=1;i<=n;i++)
	{
		ans=0;
		if(q[i]>0)ans+=ins(q[i]);
		else
		{
			if(q[i]==-2)
			{
				int  x=*fuck.begin();
				ans+=del(x);
				fuck.insert(x);
				if(!root)root=x;
				else  fa[root]=x,son[x][1]=root,root=x;
				change1(1,1,qlen,1,qlen,1);
				change2(1,1,qlen,root,1);
			}
			else  if(q[i]==-3)
			{
				int  x=*(--fuck.end());
				ans+=del(x);
				fuck.insert(x);
				if(!root)root=x;
				else  fa[root]=x,son[x][0]=root,root=x;
				change1(1,1,qlen,1,qlen,1);
				change2(1,1,qlen,root,1);
			}
			else  if(q[i]==-4)ans+=del(*fuck.begin());
			else  ans+=del(*(--fuck.end()));
		}
		printf("%d\n",ans);
	}
	return  0;
}
posted @ 2020-11-05 12:14  敌敌畏58  阅读(104)  评论(0编辑  收藏  举报