P6136 【模板】普通平衡树(数据加强版)
P6136 【模板】普通平衡树(数据加强版)
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些整数,其中需要提供以下操作:
- 插入一个整数
。 - 删除一个整数
(若有多个相同的数,只删除一个)。 - 查询整数
的排名(排名定义为比当前数小的数的个数 )。 - 查询排名为
的数(如果不存在,则认为是排名小于 的最大数。保证 不会超过当前数据结构中数的总数)。 - 求
的前驱(前驱定义为小于 ,且最大的数)。 - 求
的后继(后继定义为大于 ,且最小的数)。
本题强制在线,保证所有操作合法(操作
输入格式
第一行两个正整数
第二行
接下来
我们记
输出格式
输出一行一个整数,表示所有
限制与约定
对于
因为
我调了一下午spaly没调出来
我实在是太想学FHQ-Treap了
所以这是一篇FHQ-Treap的总结:
FHQ-Treap只需要支持两种操作:分裂和合并
所以十分的好写(或许)
首先介绍一下FHQ-Treap需要的数组:
siz ; val ; pri: 分别表示以 x 为根的子树的大小,节点 x 的值,节点 x 的优先级(由 rand() 产生,其目的在维持树的平衡)
ch[N][2]: 节点x的左右儿子的位置(感觉有点像 0/1Tried 的写法)
分裂:
split(int x,int k) 表示将以
(x中的节点按照与k的大小关系被分入a,b中)
Code:
pi split(int x,int k) { if(!x)return pi(0,0); if(val[x]<k)//完全在右子树 { pi y=split(ch[x][1],k); ch[x][1]=y.a;//将分裂后的左子树与x合并(k不在y.a中) upd(x); return pi(x,y.b);//最后分裂出了 x y.b 返回这两棵树 } else//存在于左子树内 { pi y= split(ch[x][0],k); ch[x][0]=y.b; upd(x); return pi(y.a,x); } }
合并:
int merge(int x,int y) 表示将两棵树x,y合并将新的根节点返回
Code:
int merge(int x,int y) { if(!x||!y)return x+y; if(pri[x]<pri[y])//有点左偏树的味道 { ch[x][1]=merge(ch[x][1],y); upd(x); return x; } else { ch[y][0]=merge(x,ch[y][0]);//始终满足y子树内任意值比x大 upd(y); return y; } }
插入:
先新建一个节点表示k,其下标为++cnt
insert(int k):
将rt按照k拆成两棵树x,y
然后合并merge(merge(x,cnt),y)
void insert(int k) { val[++cnt]=k;pri[cnt]=rand()*5+rand();siz[cnt]=1; pi x=split(rt,k); rt=merge(merge(x.a,cnt),x.b); }
删除:
void del(int k) { pi x,y; x=split(rt,k);//x.a:[0,k-1] x.b:[k,inf] y=split(x.b,k+1);//y.a[k,k] y.b:[k+1,inf] y.a=merge(ch[y.a][0],ch[y.a][1]); x.b=merge(y.a,y.b); rt=merge(x.a,x.b); }
查询系列操作:
int Rank(int k) { int res=0; pi x=split(rt,k); res=siz[x.a]+1; merge(x.a,x.b); return res; } int kth(int x,int k) { if(k==siz[ch[x][0]]+1)return val[x]; if(k<=siz[ch[x][0]])return kth(ch[x][0],k); return kth(ch[x][1],k-(siz[ch[x][0]]+1)); } int pre(int x){int k=Rank(x)-1;return kth(rt,k);}; int suf(int x){int k=Rank(x+1);return kth(rt,k);};
然后这题就做完了
Code:
#include<bits/stdc++.h> #include<time.h> #include<ctime> const int N=1.2e6+5; using namespace std; int n,m,cnt,rt; int siz[N],val[N],pri[N]; int ch[N][2]; struct pi{ int a,b; pi(int a_=0,int b_=0) { a=a_,b=b_; } }; void upd(int x){siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;}; pi split(int x,int k) { if(!x)return pi(0,0); if(val[x]<k)//完全在右子树 { pi y=split(ch[x][1],k); ch[x][1]=y.a;//将分裂后的左子树与x合并(k不在y.a中) upd(x); return pi(x,y.b);//最后分裂出了 x y.b 返回这两棵树 } else//存在于左子树内 { pi y= split(ch[x][0],k); ch[x][0]=y.b; upd(x); return pi(y.a,x); } } int merge(int x,int y) { if(!x||!y)return x+y; if(pri[x]<pri[y])//有点左偏树的味道 { ch[x][1]=merge(ch[x][1],y); upd(x); return x; } else { ch[y][0]=merge(x,ch[y][0]);//始终满足y子树内任意值比x大 upd(y); return y; } } void insert(int k) { val[++cnt]=k;pri[cnt]=rand()*5+rand();siz[cnt]=1; pi x=split(rt,k); rt=merge(merge(x.a,cnt),x.b); } void del(int k) { pi x,y; x=split(rt,k);//x.a:[0,k-1] x.b:[k,inf] y=split(x.b,k+1);//y.a[k,k] y.b:[k+1,inf] y.a=merge(ch[y.a][0],ch[y.a][1]); x.b=merge(y.a,y.b); rt=merge(x.a,x.b); } int Rank(int k) { int res=0; pi x=split(rt,k); res=siz[x.a]+1; merge(x.a,x.b); return res; } int kth(int x,int k) { if(k==siz[ch[x][0]]+1)return val[x]; if(k<=siz[ch[x][0]])return kth(ch[x][0],k); return kth(ch[x][1],k-(siz[ch[x][0]]+1)); } int pre(int x){int k=Rank(x)-1;return kth(rt,k);}; int suf(int x){int k=Rank(x+1);return kth(rt,k);}; int main() { //freopen("P6136.in","r",stdin); cin>>n>>m; srand(clock()); for(int i=1,x;i<=n;i++) { scanf("%d",&x); insert(x); } int ans=0,last=0; for(int i=1,opt,x;i<=m;i++) { scanf("%d%d",&opt,&x); x^=last; switch(opt) { case 1: insert(x);break; case 2: del(x);break; case 3: last=Rank(x);ans^=last;break; case 4: last=kth(rt,x);ans^=last;break; case 5: last=pre(x);ans^=last;break; case 6: last=suf(x);ans^=last;break; } } printf("%d",ans); }