BZOJ3223 文艺平衡树

Description

  您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

Input

  第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数
  接下来m行每行两个数[L,R] 数据保证 1<=L<=R<=n

Output

  输出一行n个数字,表示原始序列经过m次变换后的结果

Sample Input

5 3

1 3

1 3

1 4

Sample Output

4 3 2 1 5

Hint

1≤n,m≤100000

嘛,又是一道题调一上午系列~

简单说一下区间操作,假设操作区间[L,r]。

首先明确无论怎么旋转,BST的中序遍历是不会改变的,那么我们通过伸展操作一定能得到如下图的结构:

我们发现,将L-1伸展到根,R+1伸展到根的右儿子时,上图的红色三角形就是[L,r]区间A.A,然后我们只需要像线段树一样乱搞就可以了。

//翻转之后位置会改变,但是我们可以通过size值求出它的位置 
#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,R) for(register int i=(L);i<=(R);++i)
#define Red(j,R,L) for(register int j=(R);j>=(L);--j)
const int N = 1e5+10;
int n,m,a[N];
struct Splay{
	bool rev[N];//翻转标记 
	int k[N],siz[N];
	int rt,cnt,p[N],ch[N][2];
	#define Ls(v) ch[v][0]
	#define rs(v) ch[v][1]
	#define sum(v) siz[v]=siz[Ls(v)]+siz[rs(v)]+1
	inline void pushdown(int x){
		if(rev[x]){
			rev[Ls(x)]^=1,rev[rs(x)]^=1;
			swap(Ls(x),rs(x));
			rev[x]=0;
		}
	}
	inline void rot(int x){
		int f=p[x],gf=p[f],type=rs(f)==x,son=ch[x][!type];
		ch[p[son]=f][type]=son,sum(f);
		ch[p[f]=x][!type]=f,sum(x);
		ch[p[x]=gf][rs(gf)==f]=x;
	}
//	int top,stk[N];
	inline void splay(int x,int goal){//旋转在目标的下面
//		stk[++top]=x;
//		if(p[x])for(int i=x;p[i];i=p[i])stk[++top]=p[i];
//		while(top)pushdown(stk[top--]);
//		提供两种写法,上面是真正的自顶向下, 不过下面因为有三个pushdown,彼此间的“无用标记”会抵消= =
//      上面的要满慢上一些 
		while(p[x]^goal){
			if(p[p[x]])pushdown(p[p[x]]);//什么叫自顶向下 
			if(p[x])pushdown(p[x]);//这就叫自顶向下? 
			if(x)pushdown(x);//对于不清楚的地方,哈起下传标记就可以了 
			if((p[p[x]]^goal)&&((rs(p[p[x]])==p[x])==(rs(p[x])==x)))rot(p[x]);
			rot(x);
		}
		if(!goal)rt=x;
	}
	inline int build(int L,int r){
		if(L>r)return 0;
		int x=++cnt,Mid=L+r>>1;
		k[x]=a[Mid];
		siz[x]=1;
		Ls(x)=build(L,Mid-1);//我们是按照点建值,注意Mid-1 
		rs(x)=build(Mid+1,r);
		if(Ls(x))p[Ls(x)]=x;
		if(rs(x))p[rs(x)]=x;
		sum(x);
		return x;
	}
	inline int FindPos(int kth){
		int x=rt;
		while(1){
			pushdown(x);
			if(siz[Ls(x)]+1==kth)break;
			if(siz[Ls(x)]+1<kth)kth-=siz[Ls(x)]+1,x=rs(x);
			else x=Ls(x);
		}
		return x;
	}
	inline void reverse(int L,int r){
		int Lp=FindPos(L),rp=FindPos(r);
		splay(Lp,0),splay(rp,rt);
		rev[Ls(rp)]^=1;
	}
}sp;
inline void init(){
	scanf("%d%d",&n,&m);
	a[1]=-0x3f3f3f3f;//有可能操作的是[1,k]or[k,n]区间,因此要加哨兵 
	Inc(i,1,n)a[i+1]=i;
	a[n+2]=0x3f3f3f3f;
	sp.rt=sp.build(1,n+2);
}
inline void solv(){
	while(m--){
		int L,r;scanf("%d%d",&L,&r);
		sp.reverse((L+1)-1,(r+1)+1);
	}
	Inc(i,2,n+1)cout<<sp.k[sp.FindPos(i)]<<" ";
}
int main(){
	init();
	solv();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值