Skip to content

平衡树 ​

Treap ​

模板 ​

cpp
struct Node{
    int l,r;//左右儿子
    int key,val;//key为二叉搜索树的权值,val为堆的权值
    int cnt,size;//cnt为当前为key的个数,size为子树大小
} t[N];
int root,idx;
void pushup(int p){
    t[p].size=t[t[p].l].size+t[t[p].r].size+t[p].cnt;
}
int newNode(int key){
    t[++idx].key=key;
    t[idx].val=rand();
    t[idx].cnt=t[idx].size=1;
    return idx;
}
void build(){
    newNode(-INF),newNode(INF);
    root=1,t[1].r=2;//赋值哨兵,防止越界访问
    pushup(root);
}
void zig(int &p){//右旋
    int q=t[p].l;
    t[p].l=t[q].r,t[q].r=p,p=q;
    pushup(t[p].r);
    pushup(p);
}
void zag(int &p){//左旋
    int q=t[p].r;
    t[p].r=t[q].l,t[q].l=p,p=q;
    pushup(t[p].l);
    pushup(p);
}
void insert(int &p,int key){
    if(!p)p=newNode(key);
    else if(t[p].key==key)t[p].cnt++;
    else if(t[p].key>key){
        insert(t[p].l,key);
        if(t[t[p].l].val>t[p].val)zig(p);
    }
    else{
        insert(t[p].r,key);
        if(t[t[p].r].val>t[p].val)zag(p);
    }
    pushup(p);
}
void remove(int &p,int key){
    if(!p)return;
    if(t[p].key==key){
        if(t[p].cnt>1)t[p].cnt--;
        else if(t[p].l||t[p].r){
            if(!t[p].r||t[t[p].l].val>t[t[p].r].val){
                zig(p);//右旋之后的p为左儿子,p->t[p].r
                remove(t[p].r,key);
            }
            else{
                zag(p);
                remove(t[p].l,key);
            }
        }
        else p=0;
    }
    else if(t[p].key>key)remove(t[p].l,key);
    else remove(t[p].r,key);
    pushup(p);
}
int get_rank_by_key(int p,int key){
    if(!p)return 1;
    if(t[p].key==key)return t[t[p].l].size+1;
    if(t[p].key>key)return get_rank_by_key(t[p].l,key);
    return t[t[p].l].size+t[p].cnt+get_rank_by_key(t[p].r,key);
}
int get_key_by_rank(int p,int rank){
    if(!p)return INF;
    if(t[t[p].l].size>=rank)return get_key_by_rank(t[p].l,rank);
    if(t[t[p].l].size+t[p].cnt>=rank)return t[p].key;
    return get_key_by_rank(t[p].r,rank-t[t[p].l].size-t[p].cnt); 
}
int get_prev(int p,int key){//找到严格小于key的最大值
    if(!p)return -INF;
    if(t[p].key>=key)return get_prev(t[p].l,key);
    return max(t[p].key,get_prev(t[p].r,key));
}
int get_next(int p,int key){//找到严格大于key的最小值
    if(!p)return INF;
    if(t[p].key<=key)return get_next(t[p].r,key);
    return min(t[p].key,get_next(t[p].l,key));
}
void solve(){
    build();
    int q;
    cin>>q;
    while(q--){
        int op,x;
        cin>>op>>x;
        if(op==1)insert(root,x);
        else if(op==2)remove(root,x);
        else if(op==3)cout<<get_rank_by_key(root,x)-1<<"\n";//有一个负无穷,需要减一,下面同理
        else if(op==4)cout<<get_key_by_rank(root,x+1)<<"\n";
        else if(op==5)cout<<get_prev(root,x)<<"\n";
        else cout<<get_next(root,x)<<"\n";
    }
}

pbds ​

cpp
#include<bits/extc++.h>
using namespace __gnu_pbds;
#define TR tree<PII,null_type,less<PII>,rb_tree_tag,tree_order_statistics_node_update>
void solve(){
    TR tr;
    int q;
    cin>>q;
    int cnt=0;
    tr.insert({-INF,0});
    tr.insert({INF,0});
    while(q--){
        int op,x,ans;
        cin>>op>>x;
        if(op==1)tr.insert({x,++cnt});//pbds平衡树无法记录个数,因此使用pair,第二维代表编号
        else if(op==2)tr.erase(*tr.lower_bound({x,0}));
        else if(op==3)cout<<tr.order_of_key(*tr.lower_bound({x,0}))<<"\n";
        else if(op==4)cout<<tr.find_by_order(x)->first<<"\n";
        else if(op==5)cout<<(--tr.lower_bound({x,0}))->first<<"\n";
        else cout<<(tr.upper_bound({x,INF})->first)<<"\n";
    }
}

splay ​

FHQ-Treap ​

按值分裂 ​

cpp
mt19937_64 rnd(114514);
struct FHQ{
    int ls,rs,key,sz,val;
} fhq[N];s
int root,T1,T2,T3,idx;
int newNode(int val){
    fhq[++idx]={0,0,(int)rnd(),1,val};
    return idx;
}
void pushup(int u){
    fhq[u].sz=fhq[fhq[u].ls].sz+fhq[fhq[u].rs].sz+1;
}
void split(int u,int val,int &x,int &y){
    if(!u){
        x=y=0;
        return;
    }
    if(fhq[u].val>val){
        y=u;
        split(fhq[u].ls,val,x,fhq[u].ls);
    }
    else{
        x=u;
        split(fhq[u].rs,val,fhq[u].rs,y);
    }
    pushup(u);
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(fhq[x].key>fhq[y].key){
        fhq[x].rs=merge(fhq[x].rs,y);
        pushup(x);
        return x;
    }
    fhq[y].ls=merge(x,fhq[y].ls);
    pushup(y);
    return y;
}
void insert(int val){
    split(root,val,T1,T2);
    root=merge(merge(T1,newNode(val)),T2);
}
void erase(int x){
    split(root,x,T1,T2);
    split(T1,x-1,T1,T3);
    T3=merge(fhq[T3].ls,fhq[T3].rs);
    root=merge(merge(T1,T3),T2);
}
int find_rank(int x){
    split(root,x-1,T1,T2);
    int res=fhq[T1].sz+1;
    root=merge(T1,T2);
    return res;
}
int kth(int k){
    int u=root;
    while(u){
        int tmp=fhq[fhq[u].ls].sz+1;
        if(tmp==k)break;
        if(k<tmp)u=fhq[u].ls;
        else{
            k-=tmp;
            u=fhq[u].rs;
        }
    }
    return fhq[u].val;
}
int find_prev(int u,int val){
    if(u==0)return -INF;
    if(fhq[u].val<val){
        int res=find_prev(fhq[u].rs,val);
        return res==-INF?fhq[u].val:res;
    }
    return find_prev(fhq[u].ls,val);
}
int find_next(int u,int val){
    if(u==0)return INF;
    if(fhq[u].val>val){
        int res=find_next(fhq[u].ls,val);
        return res==INF?fhq[u].val:res;
    }
    return find_next(fhq[u].rs,val);
}
void solve(){
    int n;
    cin>>n;
    while(n--){
        int op,x;
        cin>>op>>x;
        if(op==1)insert(x);
        else if(op==2)erase(x);
        else if(op==3)cout<<find_rank(x)<<"\n";
        else if(op==4)cout<<kth(x)<<"\n";
        else if(op==5)cout<<find_prev(root,x)<<"\n";
        else cout<<find_next(root,x)<<"\n";
    }
}

按排名分裂 ​

cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+10,M=6010;
const int mod=1e9+7;
const int INF=1e18;
typedef pair<int,int> PII;
mt19937_64 rnd(114514);
struct FHQ{
    int ls,rs,key,sz,val;
} fhq[N];
int root,T1,T2,T3,idx;
int newNode(int val){
    fhq[++idx]={0,0,(int)rnd(),1,val};
    return idx;
}
void pushup(int u){
    fhq[u].sz=fhq[fhq[u].ls].sz+fhq[fhq[u].rs].sz+1;
}
void split(int u,int k,int &x,int &y){
    if(!u){
        x=y=0;
        return;
    }
    int tmp=fhq[fhq[u].ls].sz+1;
    if(k==tmp){
        x=u;
        y=fhq[u].rs;
        fhq[u].rs=0;
    }
    else if(k<tmp){
        y=u;
        split(fhq[u].ls,k,x,fhq[u].ls);
    }
    else{
        x=u;
        split(fhq[u].rs,k-tmp,fhq[u].rs,y);
    }
    pushup(u);
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(fhq[x].key>fhq[y].key){
        fhq[x].rs=merge(fhq[x].rs,y);
        pushup(x);
        return x;
    }
    fhq[y].ls=merge(x,fhq[y].ls);
    pushup(y);
    return y;
}
void solve(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        root=merge(root,newNode(i));
    }
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        x%=fhq[root].sz;
        split(root,x,T1,T2);
        split(T2,1,T2,T3);
        root=merge(T3,T1);
        cout<<fhq[T2].val<<"\n";
    }
}
signed main(){
    cin.tie(0)->sync_with_stdio(0);
    int T=1;
    // cin>>T;
    while(T--)solve();
    return 0;
}

文艺平衡树,按排名分裂 ​

cpp
mt19937_64 rnd(114514);
struct FHQ{
    int ls,rs,key,sz,val;
    bool tag;
} fhq[N];
int root,T1,T2,T3,idx;
int newNode(int val){
    fhq[++idx]={0,0,(int)rnd(),1,val};
    return idx;
}
void pushup(int u){
    fhq[u].sz=fhq[fhq[u].ls].sz+fhq[fhq[u].rs].sz+1;
}
void pushdown(int u){
    if(fhq[u].tag){
        swap(fhq[u].ls,fhq[u].rs);
        if(fhq[u].ls)fhq[fhq[u].ls].tag^=1;
        if(fhq[u].rs)fhq[fhq[u].rs].tag^=1;
        fhq[u].tag=0;
    }
}
void split(int u,int k,int &x,int &y){
    if(!u){
        x=y=0;
        return;
    }
    pushdown(u);
    int tmp=fhq[fhq[u].ls].sz+1;
    if(k==tmp){
        x=u;
        y=fhq[u].rs;
        fhq[u].rs=0;
    }
    else if(k<tmp){
        y=u;
        split(fhq[u].ls,k,x,fhq[u].ls);
    }
    else{
        x=u;
        split(fhq[u].rs,k-tmp,fhq[u].rs,y);
    }
    pushup(u);
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(fhq[x].key>fhq[y].key){
        pushdown(x);
        fhq[x].rs=merge(fhq[x].rs,y);
        pushup(x);
        return x;
    }
    pushdown(y);
    fhq[y].ls=merge(x,fhq[y].ls);
    pushup(y);
    return y;
}
void reverse(int l,int r){
    split(root,l-1,T1,T2);
    split(T2,r-l+1,T2,T3);
    fhq[T2].tag^=1;
    root=merge(merge(T1,T2),T3);
}
void dfs(int u){
    if(!u)return;
    pushdown(u);
    dfs(fhq[u].ls);
    cout<<fhq[u].val<<" ";
    dfs(fhq[u].rs);
}
void solve(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        root=merge(root,newNode(i));
    }
    while(m--){
        int l,r;
        cin>>l>>r;
        reverse(l,r);
    }
    dfs(root);
}

P3224 [HNOI2012] 永无乡 ​

题意:
n座桥,每座桥有个排名,初始有m条边,多次询问
B x y在岛x和y之间修桥
Q x k与x连通的第k小的岛
思路:

  • 并查集维护连通性
  • 平衡树本身不能合并,因此采取启发式合并,遍历小节点

P4402 [Cerc2007] robotic sort 机械排序(文艺平衡树) ​

题意:
每次找出第k小值的下标pos,然后翻转[i,pos],问翻转前第k小值的下标
思路:

  • 显然这是个排序操作,每次都能让第k小放到对应的位置
  • fhq,点内维护一个mn值,然后每次可以查询与root相同的mn值的最小点,它的排名即当前第k小的排名
  • 因为每次会让最小值向前,可以认为删除该最小值
  • 所以每次查询最小值的排名,并反转即可