题目背景
这是一道经典的Splay模板题——文艺平衡树。

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

输入格式
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2, \cdots n-1,n)(1,2,⋯n−1,n) m表示翻转操作次数

接下来m行每行两个数 [l,r][l,r] 数据保证 1 \leq l \leq r \leq n1≤l≤r≤n

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

输入输出样例
输入 #1复制

5 3
1 3
1 3
1 4
输出 #1复制
4 3 2 1 5
说明/提示
n, m \leq 100000n,m≤100000


题目大意:就是给你一个区间,m次操作,每次反转一个区间,求最后区间的状态。


这道题虽然是splay的模板题,但是我们并不想用splay做(因为不会),于是我们可以考虑用无旋treap来做区间操作。


我们对无旋treap打上懒标记,每次记录一下即可。要注意每次split子树时,先传递懒标记,防止树被分裂之后,懒标记无法正确向下传递。

最后输出答案,一次dfs即可。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10;
int n,m;
int ch[N][2],sz[N],val[N],pri[N],lazy[N],num,root,x,y,z;
inline void push_up(int p){
	sz[p]=sz[ch[p][0]]+sz[ch[p][1]]+1;
}
inline void push_down(int p){
	if(!lazy[p])	return ;	swap(ch[p][0],ch[p][1]);
	lazy[ch[p][0]]^=1;	lazy[ch[p][1]]^=1;	lazy[p]=0;
}
inline int new_node(int v){
	sz[++num]=1; val[num]=v; pri[num]=rand(); return num;
}
void split(int now,int k,int &x,int &y){
	if(!now)	return (void)(x=y=0);
	push_down(now);
	if(k<=sz[ch[now][0]])	y=now,split(ch[now][0],k,x,ch[now][0]);
	else	x=now,split(ch[now][1],k-sz[ch[now][0]]-1,ch[now][1],y);
	push_up(now);
}
int merge(int x,int y){
	if(!x||!y)	return x+y;
	push_down(x);	push_down(y);
	if(pri[x]<pri[y]){
		ch[x][1]=merge(ch[x][1],y);	push_up(x);	return x;
	}else{
		ch[y][0]=merge(x,ch[y][0]);	push_up(y);	return y;
	}
}
inline void reverse(int l,int r){
	split(root,l-1,x,y);	split(y,r-l+1,y,z);
	lazy[y]^=1;	root=merge(merge(x,y),z);
}
void dfs(int x){
	if(!x)	return ;
	push_down(x);
	dfs(ch[x][0]);
	printf("%d ",val[x]);
	dfs(ch[x][1]);
}
signed main(){
	srand((unsigned)time(NULL));
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++)	root=merge(root,new_node(i));	
	while(m--){
		int l,r;	scanf("%d %d",&l,&r);	reverse(l,r);
	}
	dfs(root);puts("");
	return 0;
}