没有学习过 fhq Treap 的可以看我上一篇文章,看过的建议去再看看分裂和合并操作
在上一篇文章中提到,fhq Treap 可以支持比较多的操作,文艺平衡树就是其中一种,其实就是可以实现区间操作(翻转)的平衡树
板子在这里
fhq Treap 实现文艺平衡树的步骤是:
这里用到了线段树懒标记的思想,没有学过线段树的看这里
不懂为什么按大小分裂可以的先别着急,接着往下看
和前面讲的是一样的,就不赘述了
void push_up(int pos){
fhq[pos].siz = fhq[fhq[pos].l].siz + fhq[fhq[pos].r].siz + 1;
}
和线段树一样的,如果当前位置存在翻转,那就翻转掉,然后把标记下传到左右子树去,记得清空懒标记
这里使用异或,也就是原来翻转的现在不翻转了,原来不翻转的现在翻转了
void push_down(int pos){
if(fhq[pos].tag){
swap(fhq[pos].l,fhq[pos].r);
fhq[fhq[pos].l].tag ^= 1;
fhq[fhq[pos].r].tag ^= 1;
fhq[pos].tag = 0;
}
}
关于 push_down 放在哪里,在要修改当前节点的时候就下传,如果你分不清,还有一种方法是哪哪都放一个
这里是按照大小分裂,其实可以理解为按照 rank 分裂,因为如果画张图会发现,一开始的树的中序遍历其实就是原序列,所以按照大小其实就是按照原来的 rank 去把区间取出来再进行操作
个人感觉可以参考上一篇 查询排名为 rank 的值 这个函数来理解
void split(int pos, int siz , int &x, int &y){
if(!pos){
x = y = 0;
return;
}
push_down(pos);
if(fhq[fhq[pos].l].siz < siz){
x = pos;
split(fhq[pos].r,siz - fhq[fhq[pos].l].siz-1,fhq[pos].r,y);
}else{
y = pos;
split(fhq[pos].l,siz,x,fhq[pos].l);
}
push_up(pos);
}
可能把 s i z siz siz 改成 r a n k rank rank 会更好理解一点?
合并其实也就是多了个 push_down 其他没啥区别
int merge(int x, int y){
if(!x || !y) return x+y;
if(fhq[x].key < fhq[y].key){
push_down(x);
fhq[x].r = merge(fhq[x].r,y);
push_up(x);
return x;
}else{
push_down(y);
fhq[y].l = merge(x,fhq[y].l);
push_up(y);
return y;
}
}
翻转直接按照上面说的步骤来就可以了
void reverse(int l, int r){
int x,y,z;
split(root,l-1,x,y);
split(y,r-l+1,y,z);
fhq[y].tag ^= 1;
root = merge(merge(x,y),z);
}
上面提到,没经过操作的树的中序遍历就是原序列,所以同理的,操作之后的树也输出中序遍历就可以了
void print(int pos){
if(!pos) return;
push_down(pos);
print(fhq[pos].l);
cout << fhq[pos].val << " ";
print(fhq[pos].r);
}
这里构造原序列的时候,因为有一个性质:序列是顺序的,所以后加入的节点一定比前面的节点都大,所以直接合并就可以了
#include
const int N = 1e5+10;
using namespace std;
int n,m;
struct treap{
int val,key,siz,l,r;
bool tag;
}fhq[N];
int cnt,root;
int new_treap(int val){
fhq[++cnt].val = val;
fhq[cnt].siz = 1;
fhq[cnt].key = rand();
return cnt;
}
void push_up(int pos){
fhq[pos].siz = fhq[fhq[pos].l].siz + fhq[fhq[pos].r].siz + 1;
}
void push_down(int pos){
if(fhq[pos].tag){
swap(fhq[pos].l,fhq[pos].r);
fhq[fhq[pos].l].tag ^= 1;
fhq[fhq[pos].r].tag ^= 1;
fhq[pos].tag = 0;
}
}
void split(int pos, int siz , int &x, int &y){
if(!pos){
x = y = 0;
return;
}
push_down(pos);
if(fhq[fhq[pos].l].siz < siz){
x = pos;
split(fhq[pos].r,siz - fhq[fhq[pos].l].siz-1,fhq[pos].r,y);
}else{
y = pos;
split(fhq[pos].l,siz,x,fhq[pos].l);
}
push_up(pos);
}
int merge(int x, int y){
if(!x || !y) return x+y;
if(fhq[x].key < fhq[y].key){
push_down(x);
fhq[x].r = merge(fhq[x].r,y);
push_up(x);
return x;
}else{
push_down(y);
fhq[y].l = merge(x,fhq[y].l);
push_up(y);
return y;
}
}
void reverse(int l, int r){
int x,y,z;
split(root,l-1,x,y);
split(y,r-l+1,y,z);
fhq[y].tag ^= 1;
root = merge(merge(x,y),z);
}
void print(int pos){
if(!pos) return;
push_down(pos);
print(fhq[pos].l);
cout << fhq[pos].val << " ";
print(fhq[pos].r);
}
int main(){
srand(time(0));
cin >> n>> m;
for(int i = 1;i <= n; i++){
root = merge(root,new_treap(i));
}
while(m--){
int l,r;
cin >> l >> r;
reverse(l,r);
}
print(root);
return 0;
}
有任何疑问或不足之处,欢迎在评论区指出