平衡树
普通平衡树
我们这里着重介绍一下
首先我们会用一个结构体存下平衡树的节点。这道题中需要存左右儿子编号,优先度(随机的一个值),点上存的数是多少,子树中有多少数。我们记作
接下来我们一个一个讲解函数:
新开点函数
考虑一个新点的五个值分别为
int newnode(int val){
tr[++cnt]={0,0,rand(),val,1};
return cnt;
}
上传函数
事实上,只有
void pushup(int p){
tr[p].siz=tr[tr[p].l].siz+tr[tr[p].r].siz+1;
}
分裂函数
同机房老哥写的
我们考虑按权值
首先如果遇到空节点直接返回即可。然后如果当前节点的值小于
void split(int p,int &a,int &b,int val){
if(p==0){
a=b=0;
return;
}
if(tr[p].da<=val){
a=p;
split(tr[p].r,tr[a].r,b,val);
}
else{
b=p;
split(tr[p].l,a,tr[b].l,val);
}
pushup(p);
}
合并函数
首先基本对于所有数据结构的合并,都有在合并的两点其中一个为空时返回另一个(都空就返回空)。
然后我们维护的优先级
假设我们要合并
int merge(int a,int b){
if(!a||!b)return a+b;
if(tr[a].rd<tr[b].rd){
tr[a].r=merge(tr[a].r,b);
pushup(a);
return a;
}
else{
tr[b].l=merge(a,tr[b].l);
pushup(b);
return b;
}
}
插入操作
因为下面的操作都比较简单,故代码统一在最后给出。
注意:所有在下文提到的原树按
假设我们插入
删除操作
首先还是把原树按删除值
这时显然
最后再把所有东西原样合并回去即可。
查找排名操作
考虑把原树按照查找值
根据排名找数操作
考虑一个平衡树上二分,我们设查的排名为
-
,返回当前点的值。 -
,在右子树中 。 -
,在左子树中 。
查找前驱操作
考虑把原树按照
查找后继操作
考虑把原树按照
完整代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
struct fhq{
int rt,cnt;
struct node{
int l,r,rd,da,siz;
}tr[N];
int newnode(int val){
tr[++cnt]={0,0,rand(),val,1};
return cnt;
}
void pushup(int p){
tr[p].siz=tr[tr[p].l].siz+tr[tr[p].r].siz+1;
}
void split(int p,int &a,int &b,int val){
if(p==0){
a=b=0;
return;
}
if(tr[p].da<=val){
a=p;
split(tr[p].r,tr[a].r,b,val);
}
else{
b=p;
split(tr[p].l,a,tr[b].l,val);
}
pushup(p);
}
int merge(int a,int b){
if(!a||!b)return a+b;
if(tr[a].rd<tr[b].rd){
tr[a].r=merge(tr[a].r,b);
pushup(a);
return a;
}
else{
tr[b].l=merge(a,tr[b].l);
pushup(b);
return b;
}
}
void ins(int val){
int l,r;
split(rt,l,r,val);
rt=merge(merge(l,newnode(val)),r);
}
void del(int val){
int l,r,ll,lr;
split(rt,l,r,val);
split(l,ll,lr,val-1);
lr=merge(tr[lr].l,tr[lr].r);
rt=merge(merge(ll,lr),r);
}
int get_rk(int val){
int l,r;
split(rt,l,r,val-1);
int res=tr[l].siz+1;
rt=merge(l,r);
return res;
}
int find(int p,int rk){
int siz=tr[tr[p].l].siz+1;
if(siz==rk)return tr[p].da;
else if(siz<rk)return find(tr[p].r,rk-siz);
else return find(tr[p].l,rk);
}
int get_pre(int val){
int l,r;
split(rt,l,r,val-1);
int res=find(l,tr[l].siz);
rt=merge(l,r);
return res;
}
int get_nxt(int val){
int l,r;
split(rt,l,r,val);
int res=find(r,1);
rt=merge(l,r);
return res;
}
}fhq;
signed main(){
srand(time(0));
int n;
cin>>n;
while(n--){
int op,x;
cin>>op>>x;
if(op==1)fhq.ins(x);
else if(op==2)fhq.del(x);
else if(op==3)cout<<fhq.get_rk(x)<<'\n';
else if(op==4)cout<<fhq.find(fhq.rt,x)<<'\n';
else if(op==5)cout<<fhq.get_pre(x)<<'\n';
else cout<<fhq.get_nxt(x)<<'\n';
}
return 0;
}
文艺平衡树
考虑这样一棵树,如果我们要输出他,就是输出他的中序遍历。
如果翻过来的话,就相当于反着输出中序遍历。
对于每一次操作,我们可以分裂出该区间代表的树,然后开始交换儿子。
但是发现这样巨慢无比,考虑线段树的区间修改也有这样的问题,于是我们打懒标记即可。
现在就只有一个问题,如何分裂出所在区间的树?考虑另一种经典的分裂:按照大小分裂。
我们每次分裂出前
代码:
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
struct fhq{
struct node{
int l,r,rd,da,siz,lzy;
}tr[N];
int rt=0,cnt=0;
int newnode(int val){
tr[++cnt]={0,0,rand(),val,1,0};
return cnt;
}
void pushup(int p){
tr[p].siz=tr[tr[p].l].siz+tr[tr[p].r].siz+1;
}
void pushdown(int p){
if(!tr[p].lzy)return;
swap(tr[p].l,tr[p].r);
tr[tr[p].l].lzy^=1;
tr[tr[p].r].lzy^=1;
tr[p].lzy^=1;
}
void split(int p,int &a,int &b,int siz){
if(p==0){
a=b=0;
return;
}
pushdown(p);
if(tr[tr[p].l].siz+1<=siz){
a=p;
split(tr[p].r,tr[a].r,b,siz-tr[tr[p].l].siz-1);
}
else{
b=p;
split(tr[p].l,a,tr[b].l,siz);
}
pushup(p);
}
int merge(int a,int b){
if(!a||!b)return a+b;
if(tr[a].rd<tr[b].rd){
pushdown(a);
tr[a].r=merge(tr[a].r,b);
pushup(a);
return a;
}
else{
pushdown(b);
tr[b].l=merge(a,tr[b].l);
pushup(b);
return b;
}
}
void solve(int l,int r){
int x,y,yl,yr;
split(rt,x,y,l-1);
split(y,yl,yr,r-l+1);
tr[yl].lzy^=1;
rt=merge(x,merge(yl,yr));
}
void print(int p){
if(p==0)return;
pushdown(p);
print(tr[p].l);
cout<<tr[p].da<<' ';
print(tr[p].r);
}
}fhq;
signed main(){
srand(time(0));
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
fhq.rt=fhq.merge(fhq.rt,fhq.newnode(i));
}
while(m--){
int l,r;
cin>>l>>r;
fhq.solve(l,r);
}
fhq.print(fhq.rt);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】