没有学习过 fhq Treap 的可以看我上一篇文章,看过的建议去再看看分裂和合并操作
回顾
在上一篇文章中提到,fhq Treap 可以支持比较多的操作,文艺平衡树就是其中一种,其实就是可以实现区间操作(翻转)的平衡树
文艺平衡树
板子在这里
fhq Treap 实现文艺平衡树的步骤是:
- 将树按大小分裂成小于 l l l 和 大于等于 l l l 的两颗树
- 把大的那颗树再按 r − l + 1 r-l+1 r−l+1 分裂掉
- 把得到的大小为 r − l + 1 r-l+1 r−l+1 的树打上标记
这里用到了线段树懒标记的思想,没有学过线段树的看这里
不懂为什么按大小分裂可以的先别着急,接着往下看
push_up
和前面讲的是一样的,就不赘述了
void push_up(int pos){fhq[pos].siz = fhq[fhq[pos].l].siz + fhq[fhq[pos].r].siz + 1;
}
push_down
和线段树一样的,如果当前位置存在翻转,那就翻转掉,然后把标记下传到左右子树去,记得清空懒标记
这里使用异或,也就是原来翻转的现在不翻转了,原来不翻转的现在翻转了
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);
}
Code
这里构造原序列的时候,因为有一个性质:序列是顺序的,所以后加入的节点一定比前面的节点都大,所以直接合并就可以了
#include <bits/stdc++.h>
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;
}
有任何疑问或不足之处,欢迎在评论区指出