BZOJ 3223: Tyvj 1729 文艺平衡树

学习了splay的基本插入,删除,查询第k大树等操作以后,现在来学一下区间翻转操作。
对于翻转[l,r]这个区间,操作是这样的:把第l大的数转到根节点,把第r+2的数转到根节点的左儿子,这样处理以后会发现[l,r]的这些数都在根节点的左儿子的右子树上,然后更新一下根节点的左儿子的右儿子的tag标记。
(tag标记和线段树一样,在代码中的某些地方会下传,并且会产生某些作用,现在暂时不用管它是什么作用)
对于l,r+2是这样的:l的前一个是l-1,但由于还有一个-1e8,所以是l-1+1=l;r的后一个是r+1,但由于还有一个-1e8所以是r+1+1=r+2。
听了这个操作以后,手动模拟了第一次区间翻转后,虽然没弄懂为什么这样操作是对的,以及为什么能够想出这样高深的算法,但是这样操作确实是对的。把l-1转到根,然后再把r+2转到根的左儿子,这样的话的确[l,r]区间的数均在根的左儿子的右子树上了。
但是,为什么第二次翻转还是可以这样操作呢?我很疑惑,第一次操作过以后,不是顺序改变了嘛,原来的l,r和当前的l,r不一样了呀!
既然不一样了,为什么还能kth(l),kth(r+2)呢?
想了好久,知道了这一点:区间翻转的原理没弄懂,但是它每一次对l,r进行操作的时候,只是把splay树翻来翻去,把tag标记改来改去罢了,原序列未改变。
感觉很神奇。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,l,r,ncnt,root;
int ch[N][2],size[N],cnt[N],fa[N],val[N],tag[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;
}

void dfs(int x)
{
	pushdown(x);
	if (ch[x][0]) dfs(ch[x][0]);
	if (val[x]>=1 && val[x]<=n) printf("%d ",val[x]);
	if (ch[x][1]) dfs(ch[x][1]);	
}

int main(){
	scanf("%d%d",&n,&m);
	insert(-1e8);
	for (register int i=1; i<=n; ++i) insert(i);
	insert(1e8);
	while (m--)
	{
		scanf("%d%d",&l,&r);
		reverse(l+1,r+1);	
	}
	for (register int i=1; i<=n; ++i)
	{	
		printf("%d ",val[kth(i+1)]);	
	}
	//dfs(root);
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值