BZOJ 1861: [Zjoi2006]Book 书架

感觉是一些模板操作,用区间翻转来写比合并前驱,合并后继等等简单多了,但是这题暴露了我还不是真正理解按插入顺序排序的splay树。
无注释版:
#include <bits/stdc++.h>
using namespace std;
const int N=8e4+5;
int n,m,tmp,x,s,t,ncnt,root;
char str[10];
int ch[N][2],fa[N],val[N],cnt[N],size[N],tag[N],pos[N];
int top,sta[N];

inline int chk(int x)
{
	return ch[fa[x]][1]==x;
}

inline void pushup(int x)
{
	if (!x) return;
	size[x]=size[ch[x][0]]+size[ch[x][1]]+cnt[x];
}

inline void rotate(int x)
{
	int y=fa[x],z=fa[y],k=chk(x),w=ch[x][k^1];
	ch[y][k]=w; fa[w]=y;
	ch[z][chk(y)]=x; fa[x]=z;
	ch[x][k^1]=y; fa[y]=x;
	pushup(y); pushup(x);
}

inline void pushdown(int x)
{
	if (!x) return;
	if (tag[x])
	{
		tag[ch[x][0]]^=1;
		tag[ch[x][1]]^=1;
		swap(ch[x][0],ch[x][1]);
		tag[x]=0;
	}
}

inline void splay(int x,int goal)
{
	int now=x;
	while (now)
	{
		sta[++top]=now;
		now=fa[now];
	}
	while (top) 
	{
		pushdown(sta[top]);
		top--;
	}
	while (fa[x]!=goal)
	{
		int y=fa[x],z=fa[y];
		if (z!=goal)
		{
			if (chk(x)==chk(y)) rotate(y);
			else rotate(x);
		}
		rotate(x);
	}
	if (!goal) root=x;   //!!! 又把这句话忘记了 
}

inline void insert(int x)
{
	ncnt++;
	if (root) ch[root][1]=ncnt;
	fa[ncnt]=root; val[ncnt]=x;
	ch[ncnt][0]=ch[ncnt][1]=0;
	cnt[ncnt]=size[ncnt]=1;
	splay(ncnt,0);	
}

inline int kth(int x)
{
	int cur=root;
	while (true)
	{
		pushdown(cur);
		if (x<=size[ch[cur][0]]) cur=ch[cur][0];
		else if (x>size[ch[cur][0]]+cnt[cur])
		{
			x-=size[ch[cur][0]]+cnt[cur];
			cur=ch[cur][1];	
		}
		else return cur;
	}
}

inline void reverse(int l,int r)
{
	l=kth(l-1),r=kth(r+1);
	splay(l,0); splay(r,l);
	int now=ch[ch[root][1]][0];
	if (now) tag[now]^=1;
}

int main(){
	scanf("%d%d",&n,&m);
	insert(-1e8);
	for (register int i=1; i<=n; ++i) 
	{
		scanf("%d",&tmp);
		pos[tmp]=i+1;
		insert(tmp); 
	}
	insert(1e8);
	while (m--)
	{
		scanf("%s",str); 
		scanf("%d",&x);
		if (str[0]=='T')
		{
			splay(pos[x],0);
			s=size[ch[root][0]]+1;
			if (s==2) continue;
			reverse(2,s);
			reverse(3,s);
		}
		if (str[0]=='B')
		{
			splay(pos[x],0);
			s=size[ch[root][0]]+1;
			reverse(s,n+1);
			reverse(s,n);
		}
		if (str[0]=='I')
		{
			splay(pos[x],0);
			s=size[ch[root][0]]+1;
			scanf("%d",&t);
			if (t==-1)
			{
				reverse(s-1,s);
			}
			if (t==0) continue;
			if (t==1)
			{
				reverse(s,s+1);
			}
		}
		if (str[0]=='A')
		{
			splay(pos[x],0);
			printf("%d\n",size[ch[root][0]]-1);
		}
		if (str[0]=='Q')
		{
			printf("%d\n",val[kth(x+1)]);
		}
	}
return 0;
}
注释版:
#include <bits/stdc++.h>
using namespace std;
const int N=8e4+5;
int n,m,x,l,t,ncnt,root;
int a[N],pos[N];
int dep,sta[N];
int ch[N][2],fa[N],val[N],cnt[N],size[N],tag[N];
char str[10];

inline void pushdown(int x)
{
	if (!x) return; //注意!x的时候return 
	if (tag[x])
	{
		tag[ch[x][0]]^=1;
		tag[ch[x][1]]^=1;
		tag[x]=0;
		swap(ch[x][0],ch[x][1]);
	}
}

inline int chk(int x)
{
	return ch[fa[x]][1]==x;
}

inline void pushup(int x)
{
	if (!x) return; //注意!x的时候return 
	size[x]=size[ch[x][0]]+size[ch[x][1]]+cnt[x];
}

inline void rotate(int x)
{
	int y=fa[x],z=fa[y],k=chk(x),w=ch[x][k^1];
	ch[y][k]=w; fa[w]=y;
	ch[z][chk(y)]=x; fa[x]=z;
	ch[x][k^1]=y; fa[y]=x;
	pushup(y); pushup(x);
}

inline void splay(int x,int goal)
{
    int f=x;
    sta[++dep]=f;
    while (f) 
	{
		f=fa[f];
		sta[++dep]=f;
	}
    while (dep) pushdown(sta[dep--]);
    //这个地方挺玄学的,一开始没有想到这里也要pushdown一下
	//在输出值的时候,可能会由于还有树中的tag未被pushdown完而出错,所以一个傻逼方法是每次都dfs一次
	//这样就超时了 
	//在splay旋转中pushdown,很不错,学到了
	//把根路径的点pushdown一遍的原因是:别的点与此次的splay旋转无关所以不用管 
	while (fa[x]!=goal)
	{
		int y=fa[x],z=fa[y];
		if (z!=goal)
		{
			if (chk(x)==chk(y)) rotate(y);
			else rotate(x);
		}
		rotate(x);
	}
	if (!goal) root=x;
}

inline void insert(int x)
{
//	while (ch[cur][1]) cur=ch[cur][1];
//  可以发现经过splay以后的树的形态是全部都是左儿子没有右儿子的,所以root的右儿子即为ncnt 
	ncnt++;
	if (root) ch[root][1]=ncnt;
	ch[ncnt][0]=ch[ncnt][1]=0;
	fa[ncnt]=root; val[ncnt]=x;
	cnt[ncnt]=size[ncnt]=1;
	splay(ncnt,0);
}

inline int kth(int x)
{
	int cur=root;
	while (true)
	{
		pushdown(cur);
		if (x<=size[ch[cur][0]]) cur=ch[cur][0];
		else if (x>size[ch[cur][0]]+cnt[cur])
		{
			x-=size[ch[cur][0]]+cnt[cur];
			cur=ch[cur][1];
		}
		else return cur;
	}
}

inline void work(int l,int r)
{
	l=kth(l-1); r=kth(r+1);
	splay(l,0); splay(r,l);
	int now=ch[ch[root][1]][0];
	if (now) tag[now]^=1;
	//习惯要好,当now为0的时候就不要更新了,减少不必要的锅 
}

int main(){
	//这道题目感觉各种操作都挺复杂的,但是仔细想想发现所有的操作都可以用区间翻转来实现 
	scanf("%d%d",&n,&m);
	for (register int i=1; i<=n; ++i) scanf("%d",&a[i]),pos[a[i]]=i+1;
	insert(-1e8);
	for (register int i=1; i<=n; ++i) insert(a[i]);
	insert(1e8);
	
	while (m--)
	{
		scanf("%s%d",str,&x);
		if (str[0]=='T')
		{
			splay(pos[x],0);
			l=size[ch[root][0]]+1;	
			if (l==2) continue;
			work(2,l);
			work(3,l);
		}
		if (str[0]=='B')
		{
			splay(pos[x],0);
			l=size[ch[root][0]]+1;	
			if (l==n+1) continue;
			work(l,n+1);
			work(l,n);
		}
		if (str[0]=='I')
		{
			scanf("%d",&t);
			splay(pos[x],0);
			l=size[ch[root][0]]+1;	
			if (t==-1) work(l-1,l);
			if (t==0) continue;
			if (t==1) work(l,l+1);
		}
		if (str[0]=='A')
		{
			splay(pos[x],0);
			printf("%d\n",size[ch[root][0]]-1);
		}
		if (str[0]=='Q')
		{
			printf("%d\n",val[kth(x+1)]);
		}
	}
//本来以为splay的一些基本insert,splay函数都是一样的,现在发现大错特错了,splay是很活的东西啊
//在学线段树的时候,就一开始有硬性思维想把所有东西都变成模板的样子感觉好背好写,但是数据结构是很活的
//然后这题还是有点懵懵的感觉
return 0;	
}
 
/*
10 10
1 3 2 7 5 8 10 4 9 6
Query 3
Top 5
Ask 6
Bottom 3
Ask 3
Top 6
Insert 4 -1
Query 5
Query 2
Ask 2
*/ 
/*
2
9
9
7
5
3
*/
发布了64 篇原创文章 · 获赞 29 · 访问量 695

猜你喜欢

转载自blog.csdn.net/Dove_xyh/article/details/103625036
今日推荐