文艺平衡树(区间翻转)
引入
一般像线段树这样的数据结构可以维护一些静态序列上的信息,但是如果这个序列要插入,删除或者翻转某些节点,这样就很难用那些数据结构实现了。
其实之前我很难想象平衡树能去处理序列问题,不过写完无旋Treap后,大概能理解很多
无旋Treap实现区间翻转
我们考虑一个区间翻转操作,可以被转化成这个在区间上的节点的所有子树交换左右子树,这个可以手模一下理解。
然而直接这样做甚至不如暴力,于是引入类似线段树的懒标记,就能实现单次区间翻转操作
合并和分裂要下传懒标记,分裂是按位置分裂,有一些细节要注意。
感觉这玩意拓展性很高,晚点在这里贴一些启发性的例题。
代码
#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define fi first
#define se second
#define mp make_pair
const int MAXN=100010;
struct node{
int l,r; //左右儿子
int val,siz,cnt; //真值,子树大小,这个数个数
ull pri; //大根堆优先度
bool laz; //翻转
};
struct treap{
mt19937_64 rand; //随机数生成器
int cnt,root;//节点数,根
node t[MAXN];
treap();
int newnode(int val);//生成新节点
void upd(int p);//更新子树大小
void pd(int p); //下传标记
void rev(int p); //翻转
pair<int,int> split(int p,int key);//分裂
int merge(int u,int v);//合并节点
void insert(int val);//插入节点
void reverse(int l,int r);//区间翻转
void print(int p);//输出
}tree;
template<typename T>
void read(T& x);
template<typename T>
void write(T x);
template<typename T>
void write(T x,char c);
int n,m,l,r;
int main(){
read(n); read(m);
for (int i=1;i<=n;i++) tree.insert(i);
for (int i=1;i<=m;i++){
read(l); read(r); tree.reverse(l,r);
}
tree.print(tree.root);
return 0;
}
template<typename T>
void read(T& x){
x=0; bool f=0; char ch=getchar();
while (ch>'9'||ch<'0') {
if (ch=='-') f=1;
ch=getchar();
}
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
x=f?-x:x;
}
template<typename T>
void write(T x){
if (x<0) putchar('-'),x=-x;
if (x>9) write(x/10);
putchar(x%10+'0');
}
template<typename T>
void write(T x,char c){
write(x);
putchar(c);
}
treap::treap(){
rand.seed(time(nullptr));
}
int treap::newnode(int val){ //生成新节点
++cnt;
t[cnt].val=val;
t[cnt].cnt=t[cnt].siz=1;
t[cnt].pri=rand();
return cnt;
}
void treap::upd(int p){ //更新子树大小
t[p].siz=t[t[p].l].siz+t[t[p].r].siz+t[p].cnt;
}
pair<int,int> treap::split(int p,int key){ //按照位置分裂
if (!p) return mp(0,0);
pd(p);
if (t[t[p].l].siz>=key){ //在左边,继续裂,注意要用左子树大小
auto temp=split(t[p].l,key);
t[p].l=temp.se;
upd(p);
return mp(temp.fi,p);
}
else{ //在右边,继续裂,记得差分
auto temp=split(t[p].r,key-t[t[p].l].siz-t[p].cnt);
t[p].r=temp.fi;
upd(p);
return mp(p,temp.se);
}
}
int treap::merge(int u,int v){ //合并节点
if (!u) return v;
if (!v) return u;
pd(u); pd(v);
if (t[u].pri>t[v].pri){ //满足大根堆性质,u为根
t[u].r=merge(t[u].r,v);
upd(u);
return u;
}
else {
t[v].l=merge(u,t[v].l);//v为根
upd(v);
return v;
}
}
void treap::insert(int val){//插入节点
auto temp1=split(root,val);
auto temp2=split(temp1.fi,val-1); //先分裂
if (!temp2.se) temp2.se=newnode(val);
else t[temp2.se].cnt++,upd(temp2.se);
root=merge(merge(temp2.fi,temp2.se),temp1.se); //再合并
}
void treap::rev(int p){ //翻转节点
if (!p) return;
swap(t[p].l,t[p].r);
t[p].laz^=1;
}
void treap::pd(int p){ //下传标记
if (!t[p].laz) return;
rev(t[p].l); rev(t[p].r);
t[p].laz=0;
}
void treap::reverse(int l,int r){ //区间翻转
auto temp1=split(root,l-1);
auto temp2=split(temp1.se,r-l+1); //找到要反转的区间
rev(temp2.fi);
root=merge(temp1.fi,merge(temp2.fi,temp2.se));
}
void treap::print(int p){ //输出
if (!p) return;
pd(p);
print(t[p].l);
write(t[p].val,' ');
print(t[p].r);
}