【题目来源】
https://www.acwing.com/problem/content/description/2439/
【题目描述】
给定一个长度为 n 的整数序列,初始时序列为 {1,2,…,n−1,n}。
序列中的位置从左到右依次标号为 1∼n。
我们用 [l,r] 来表示从位置 l 到位置 r 之间(包括两端点)的所有数字构成的子序列。
现在要对该序列进行 m 次操作,每次操作选定一个子序列 [l,r],并将该子序列中的所有数字进行翻转。
例如,对于现有序列 1 3 2 4 6 5 7,如果某次操作选定翻转子序列为 [3,6],那么经过这次操作后序列变为 1 3 5 6 4 2 7。
请你求出经过 m 次操作后的序列。
【输入格式】
第一行包含两个整数 n,m。
接下来 m 行,每行包含两个整数 l,r,用来描述一次操作。
【输出格式】
共一行,输出经过 m 次操作后的序列。
【数据范围】
1≤n,m≤10^5,1≤l≤r≤n
【输入样例】
6 3
2 4
1 5
3 5
【输出样例】
5 2 1 4 3 6
【算法分析】
● 目前,在信息学竞赛里,最常用的平衡树是 Splay Tree(伸展树)。Splay Tree 的最大特点是可以把某个结点旋转到指定位置,特别是可以旋转到树根的位置称为新的树根。因此,如果需要经常查询和使用某个数,可以把它旋转到根结点,这样下次访问它时,只需查一次就OK了。
Treap虽然代码简单,但功能有限,很多操作实现不了。
● Splay Tree 是动态树与树链剖分的基础。
● 左旋和右旋是 Splay Tree 的两个基本操作。它在保证 Splay Tree 的中序遍历序列不变的前提下,调整 Splay Tree 的高度,从而维护 Splay Tree 的平衡。
● 对 Splay Tree 的结点进行操作(如插入结点、查找结点)后,需将此结点旋转到 Splay Tree 的树根,并维护 Splay Tree 的中序遍历序列不变。
● Splay Tree 的核心操作是 rotate(x,k),即将结点 x 旋转到结点 k 的下方。其他所有操作,都是围绕着 rotate(x,k) 进行的。
【算法代码】
#include <iostream>
using namespace std;
const int maxn=1e5+5;
int n,m;
int root,idx;
struct splay_node {
int ch[2];
bool flag;
int size;
int val;
int fa; //father node
} tr[maxn];
void push_up(int x) {
tr[x].size=tr[tr[x].ch[0]].size+tr[tr[x].ch[1]].size+1;
}
void push_down(int x) {
if(tr[x].flag) {
swap(tr[x].ch[0],tr[x].ch[1]);
tr[tr[x].ch[0]].flag^=1,tr[tr[x].ch[1]].flag^=1;
tr[x].flag=0;
}
}
int get(int x) {
return tr[tr[x].fa].ch[1]==x;
}
void rotate(int x) {
int y=tr[x].fa,z=tr[y].fa;
int tx=get(x),ty=get(y);
tr[z].ch[ty]=x,tr[x].fa=z;
tr[y].ch[tx]=tr[x].ch[tx^1],tr[tr[x].ch[tx^1]].fa=y;
tr[x].ch[tx^1]=y,tr[y].fa=x;
push_up(y),push_up(x);
}
void splay(int x,int k) {
while(tr[x].fa!=k) {
int y=tr[x].fa,z=tr[y].fa;
if(z!=k) {
if(get(x)^get(y)) rotate (x);
else rotate(y);
}
rotate(x);
}
if(!k) root=x;
}
void insert(int val) {
int u=root,fa=0;
while(u) {
fa=u;
u=tr[u].ch[val>tr[u].val];
}
u=++idx;
if(fa) tr[fa].ch[val>tr[fa].val]=u;
tr[u].val=val,tr[u].fa=fa;
splay(u,0);
}
int get_key(int k) {
int u=root;
while(true) {
push_down(u);
if(tr[tr[u].ch[0]].size>=k) u=tr[u].ch[0];
else if(tr[tr[u].ch[0]].size+1==k) return u;
else {
k-=tr[tr[u].ch[0]].size+1;
u=tr[u].ch[1];
}
}
return -1;
}
void output(int x) {
push_down(x);
if(tr[x].ch[0]) output(tr[x].ch[0]);
if(tr[x].val>=1 && tr[x].val<=n) printf("%d ",tr[x].val);
if(tr[x].ch[1]) output(tr[x].ch[1]);
}
int main() {
scanf("%d %d",&n,&m);
for(int i=0; i<=n+1; i++) insert(i); //i<=n+1. if i<=n, TLE
while(m--) {
int le,ri;
cin>>le>>ri;
le=get_key(le),ri=get_key(ri+2);
splay(le,0), splay(ri,le);
tr[tr[ri].ch[0]].flag^=1;
}
output(root);
return 0;
}
/*
in:
6 3
2 4
1 5
3 5
out:
5 2 1 4 3 6
*/
【参考文献】
https://www.cnblogs.com/gzh-red/p/11011557.html
https://www.acwing.com/solution/content/148899/
https://www.acwing.com/blog/content/26008/
https://www.cnblogs.com/quanjun/p/16966987.html
https://www.acwing.com/solution/content/50494/
https://blog.csdn.net/qq_25426559/article/details/122710135