无旋treap
是一种诡异的平衡树
最主要的操作
就是split(拆分成两棵子树)和merge(合并为一棵子树)
update操作
这个。。。就不讲了吧。因题而异。
所以我写出来是为了什么?
split操作
分为两种
一种是以前k个和剩下的来拆分,适用于区间操作时
//r1为前k个的子树的根,r2为剩下的子树的根
void split_treap(int x,int k,int& r1,int& r2){
if(!x){
r1=0,r2=0;
return ;
}
int sz=tree[tree[x].l].sz;
if(sz>=k){
split_treap(tree[x].l,k,r1,r2);
tree[x].l=r2;
update(x);
r2=x;
}
else{
split_treap(tree[x].r,k-sz-1,r1,r2);
tree[x].r=r1;
update(x);
r1=x;
}
}
第二种是按照key值,适用于正常的平衡树
//r1为小于等于key值子树的根,r2为剩下的子树的根
void split_treap(int x,int val,int& r1,int& r2){
if(!x){
r1=0,r2=0;
return ;
}
if(tree[x].key>val){
split_treap(tree[x].l,val,r1,tree[x].l);
update(x);
r2=x;
}
else{
split_treap(tree[x].r,val,tree[x].r,r2);
update(x);
r1=x;
}
}
merge操作
把我们分出来的两个子树合在一起
注意必须当满足一棵子树中所有的key值都小于等于另一颗子树时才能合并
int merge_treap(int x,int y){
if(!x) return y;
if(!y) return x;
if(tree[x].fix<tree[y].fix){
tree[x].r=merge_treap(tree[x].r,y);
update(x);
return x;
}
else{
tree[y].l=merge_treap(x,tree[y].l);
update(y);
return y;
}
}
find_key操作
寻找第k小的值
给出一种迭代的写法
int find_key_treap(int x,int k){
if(k>tree[x].sz)
return INF;
while(1){
if(k<=tree[tree[x].l].sz)
x=tree[x].l;
else if(k==tree[tree[x].l].sz+1)
return tree[x].key;
else {
k-=tree[tree[x].l].sz+1;
x=tree[x].r;
}
}
}
find_rank操作
根据值寻找排名,如果存在多个这种值,返回最小的排名
int find_rank_treap(int x,int val){
int r1,r2;
split_treap(root,val-1,r1,r2);
int ans=tree[r1].sz+1;
root=merge_treap(r1,r2);
return ans;
}
insert操作
新建并插入值为val的节点
void insert_treap(int val){
int r1,r2;
split_treap(root,val,r1,r2);
ncnt++;
tree[ncnt]=node(val);
root=merge_treap(merge_treap(r1,ncnt),r2);
}
delete操作
删除值为val的节点
void delete_treap(int val){
int r1,r2,r3,r4;
split_treap(root,val,r1,r2);
split_treap(r1,val-1,r3,r4);
if(tree[r4].key!=val)//被删除的节点值不为val
return ;
r4=merge_treap(tree[r4].l,tree[r4].r);
root=merge_treap(merge_treap(r3,r4),r2);
}
find_previous操作
求x的前驱(前驱定义为小于x,且最大的数)
int find_previous_treap(int val){
int k=find_rank_treap(val);
return find_key_treap(k-1);
}
find_next操作
求x的后继(后继定义为大于x,且最小的数)
int find_next_treap(int val){
int r1,r2;
split_treap(root,val,r1,r2);
int ans=find_key_treap(r2,1);
root=merge_treap(r1,r2);
return ans;
}
reverse操作
把区间[l,r]翻转(比如10 30 20翻转[1,3]成20 10 30)
void reverse_treap(int l,int r){
if(l==r)
return ;
int r1,r2,r3;
split_treap(root,r,r1,r2);
split_treap(r1,l-1,r1,r3);
tree[r3].reverse^=1;//懒标记
root=merge_treap(merge_treap(r1,r3),r2);
}
addup操作
求区间[1,k]的和(怎么求[l,r]我觉得就没必要说了吧)
long long sum_treap(int x,int k){
if(!x||!k)
return 0;
if(k>tree[x].sz)
return tree[x].sum;
int sz=tree[tree[x].l].sz;
if(k<=sz)
return sum_treap(tree[x].l,k);
else
return tree[tree[x].l].sum+tree[x].key+sum_treap(tree[x].r,k-sz-1);
}
可持久化平衡树(基于无旋treap)
无旋treap相比旋转的treap,最大的区别就是不旋转(废话!)
由于不旋转,才能进行我们可持久化的改造
其实跟其他数据结构的可持久化改造类似,就是存一个root数组,并且新建节点
直接上代码吧,不懂可以看看注释
题目:洛谷3835
https://www.luogu.org/problemnew/show/3835
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
const int N=5*1e5+5,M=15000000;
struct node{
int l,r,key,fix,sz;
node(){}
node(int _key):l(0),r(0),key(_key),fix(rand()),sz(1){}
}tree[M];
int ncnt;
int root[N];
void update(int a){
tree[a].sz=1;
if(tree[a].l)
tree[a].sz+=tree[tree[a].l].sz;
if(tree[a].r)
tree[a].sz+=tree[tree[a].r].sz;
}
//为什么merge操作不需要新建节点呢,因为合并的一定是同时间的
int merge_treap(int x,int y){
if(!x) return y;
if(!y) return x;
if(tree[x].fix<tree[y].fix){
tree[x].r=merge_treap(tree[x].r,y);
update(x);
return x;
}
else{
tree[y].l=merge_treap(x,tree[y].l);
update(y);
return y;
}
}
//在split操作中新建节点
void split_treap(int x,int val,int& r1,int& r2){
if(!x){
r1=0,r2=0;
return ;
}
if(tree[x].key<=val){
split_treap(tree[x].r,val,r1,r2);
ncnt++;
tree[ncnt]=tree[x];
tree[ncnt].r=r1;
r1=ncnt;
update(ncnt);
}
else{
split_treap(tree[x].l,val,r1,r2);
ncnt++;
tree[ncnt]=tree[x];
tree[ncnt].l=r2;
r2=ncnt;
update(ncnt);
}
}
int find_key_treap(int x,int k){
while(1){
if(k<=tree[tree[x].l].sz)
x=tree[x].l;
else if(k==tree[tree[x].l].sz+1)
return tree[x].key;
else {
k-=tree[tree[x].l].sz+1;
x=tree[x].r;
}
}
}
int find_rank_treap(int&rt,int val){
int r1,r2;
split_treap(rt,val-1,r1,r2);
int ans=tree[r1].sz+1;
rt=merge_treap(r1,r2);
return ans;
}
void insert_treap(int&rt,int val){
int r1,r2;
split_treap(rt,val,r1,r2);
ncnt++;
tree[ncnt]=node(val);
rt=merge_treap(merge_treap(r1,ncnt),r2);
}
void delete_treap(int&rt,int val){
int r1,r2,r3,r4;
split_treap(rt,val,r1,r2);
split_treap(r1,val-1,r3,r4);
if(tree[r4].key==val)
r4=merge_treap(tree[r4].l,tree[r4].r);
rt=merge_treap(merge_treap(r3,r4),r2);
}
int find_previous_treap(int&rt,int val){
int k=find_rank_treap(rt,val);
return find_key_treap(rt,k-1);
}
int find_next_treap(int&rt,int val){
int r1,r2;
split_treap(rt,val,r1,r2);
int ans=find_key_treap(r2,1);
rt=merge_treap(r1,r2);
return ans;
}
int main()
{
int n;
scanf("%d",&n);
insert_treap(root[0],-2147483647);
insert_treap(root[0],2147483647);
for(int i=1;i<=n;i++){
int be,op,x;
scanf("%d%d%d",&be,&op,&x);
ncnt++;
root[i]=ncnt;
tree[ncnt]=tree[root[be]];
if(op==1)
insert_treap(root[i],x);
else if(op==2)
delete_treap(root[i],x);
else if(op==3)
printf("%d\n",find_rank_treap(root[i],x)-1);
else if(op==4)
printf("%d\n",find_key_treap(root[i],x+1));
else if(op==5)
printf("%d\n",find_previous_treap(root[i],x));
else if(op==6)
printf("%d\n",find_next_treap(root[i],x));
}
}