无旋treap树

本文详细介绍了无旋treap树的基本概念,包括数据约定、分裂、合并、插入、删除等操作,以及如何求权值为k的点的排名和排名为k的点的权值,还探讨了求前驱后继的方法。此外,提供了普通平衡树和适用于区间操作的平衡树实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

约定:

int ch[maxd][3];//0左儿子,1右儿子
int val[maxd];//权值
int rnd[maxd];//随机权值
int sze[maxd];//以每个点为根树的大小
int T,rt,x,y,z;

分裂:

inline void update(int x)
{
    sze[x]=1+sze[ch[x][0]]+sze[ch[x][1]];
}
//x为权值小于k的子树的根,y为剩下的子树的根

void split(int now,int k,int &x,int &y)//权值分裂
{
    if(!now)
    {
        x=y=0;
        return ;
    }
    else if(val[now]<=k)//它及它的左子树都小于等于k,递归向右找第二颗树
    {
        x=now;
        split(ch[now][1],k,ch[now][1],y);
    }
    else
    {
        y=now;
        split(ch[now][0],k,x,ch[now][0]);
    }
    update(now);//更新sze
}
//x为前k个的子树的根,y为剩下的子树的根

void split1(int now,int k,int &x,int &y)//单点
{
    if(!now)
    {
        x=y=0;
    }
    else{
        if(k<=sze[ch[now][0]])//左子树元素个数大于k个,将右子树全部放在第二颗树上,递归分裂左子树
        {
            y=now;
            split1(ch[now][0],k,x,ch[now][0]);
        }
        else{
            x=now;
            split1(ch[now][1],k-sze[ch[now][0]]-1,ch[now][1],y);
        }
    }
    update(now);

}

合并:

//我们假设第一棵树的权值小于第二棵树的权值,
//那么我们就比较它们的随机权值。
//如果rnd[l]<rnd[r],我们就保留它的左子树,
//另一棵树作为它的右子树;如果rnd[l]>=rnd[r],
//那我们可以保留第二棵树的右子树,另一颗树作为它的左子树。
int merge(int A,int B)
{
    if(!A||!B)
    {
        return A+B;
    }
    if(rnd[A]<rnd[B])
    {
        ch[A][1]=merge(ch[A][1],B);
        update(A);
        return A;
    }
    else
    {
        ch[B][0]=merge(A,ch[B][0]);
        update(B);
        return B;
    }
}

插入:


int new_node(int x)
{
    sze[++T]=1;
    val[T]=x;
    rnd[T]=rand();
    return T;
}
void Inser(int k)
{
    split(rt,k,x,y);
    rt=merge(merge(x,new_node(k)),y);
}

删除:

//删除权值为v的点,先把整颗树以v为权值split成两棵树a,b,再把a树按照v-1分成c,d。
//这时候值为v的点一定为d的根,
//那么我们把d的两个子儿子merge起来(划重点:这一步就是去除掉v的影响),
//再把他们重新merge起来得到一个新的树,这颗树就去除掉了v的影响。
void del(int k){
    split(rt,k,x,z);
    split(x,k-1,x,y);
    y=merge(ch[y][0],ch[y][1]);
    rt=merge(merge(x,y),z);
}

求权值为k的点的排名/排名为k的点的权值:

//直接按照a-1的权值把树分开,那么x树中最大的应该小于等于a-1,那么a的排名就是size[x]+1。
int QueryRank(int k)
{
    split(rt,k-1,x,y);
    int res=sze[x]+1;
    rt=merge(x,y);
    return res;
}
int QueryK(int now,int k)
{
    while(1)
    {
        if(k<=sze[ch[now][0]])now=ch[now][0];
        else if(k==sze[ch[now][0]]+1) return now;
        else k-=sze[ch[now][0]]+1,now=ch[now][1];
    }
    return 0;
}

求前驱后继:

int pre(int k)
{
    split(rt,k-1,x,y);
    int res=val[QueryK(x,sze[x])];
    rt=merge(x,y);
    return res;
}
int suf(int k)
{
    split(rt,k,x,y);
    int res=val[QueryK(y,1)];
    rt=merge(x,y);
    return res;
}

普通的平衡树代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
using namespace std;
const int maxd = 5e5+10;
inline int read()
{
    int x=0,f=1;char c = getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
int ch[maxd][3];//0左儿子,1右儿子
int val[maxd];//权值
int rnd[maxd];//随机权值
int sze[maxd];//以每个点为根树的大小
int T,rt,x,y,z;
inline void update(int x)
{
    sze[x]=1+sze[ch[x][0]]+sze[ch[x][1]];
}
//x为权值小于k的子树的根,y为剩下的子树的根

void split(int now,int k,int &x,int &y)//权值分裂
{
    if(!now)
    {
        x=y=0;
        return ;
    }
    else if(val[now]<=k)//它及它的左子树都小于等于k,递归向右找第二颗树
    {
        x=now;
        split(ch[now][1],k,ch[now][1],y);
    }
    else
    {
        y=now;
        split(ch[now][0],k,x,ch[now][0]);
    }
    update(now);//更新sze
}
//x为前k个的子树的根,y为剩下的子树的根

void split1(int now,int k,int &x,int &y)//单点
{
    if(!now)
    {
        x=y=0;
    }
    else{
        if(k<=sze[ch[now][0]])//左子树元素个数大于k个,将右子树全部放在第二颗树上,递归分裂左子树
        {
            y=now;
            split1(ch[now][0],k,x,ch[now][0]);
        }
        else{
            x=now;
            split1(ch[now][1],k-sze[ch[now][0]]-1,ch[now][1],y);
        }
    }
    update(now);

}
//我们假设第一棵树的权值小于第二棵树的权值,
//那么我们就比较它们的随机权值。
//如果rnd[l]<rnd[r],我们就保留它的左子树,
//另一棵树作为它的右子树;如果rnd[l]>=rnd[r],
//那我们可以保留第二棵树的右子树,另一颗树作为它的左子树。
int merge(int A,int B)
{
    if(!A||!B)
    {
        return A+B;
    }
    if(rnd[A]<rnd[B])
    {
        ch[A][1]=merge(ch[A][1],B);
        update(A);
        return A;
    }
    else
    {
        ch[B][0]=merge(A,ch[B][0]);
        update(B);
        return B;
    }
}

int new_node(int x)
{
    sze[++T]=1;
    val[T]=x;
    rnd[T]=rand();
    return T;
}
void Inser(int k)
{
    split(rt,k,x,y);
    rt=merge(merge(x,new_node(k)),y);
}
//删除权值为v的点,先把整颗树以v为权值split成两棵树a,b,再把a树按照v-1分成c,d。
//这时候值为v的点一定为d的根,
//那么我们把d的两个子儿子merge起来(划重点:这一步就是去除掉v的影响),
//再把他们重新merge起来得到一个新的树,这颗树就去除掉了v的影响。
void del(int k){
    split(rt,k,x,z);
    split(x,k-1,x,y);
    y=merge(ch[y][0],ch[y][1]);
    rt=merge(merge(x,y),z);
}
//直接按照a-1的权值把树分开,那么x树中最大的应该小于等于a-1,那么a的排名就是size[x]+1。
int QueryRank(int k)
{
    split(rt,k-1,x,y);
    int res=sze[x]+1;
    rt=merge(x,y);
    return res;
}
int QueryK(int now,int k)
{
    while(1)
    {
        if(k<=sze[ch[now][0]])now=ch[now][0];
        else if(k==sze[ch[now][0]]+1) return now;
        else k-=sze[ch[now][0]]+1,now=ch[now][1];
    }
    return 0;
}
int pre(int k)
{
    split(rt,k-1,x,y);
    int res=val[QueryK(x,sze[x])];
    rt=merge(x,y);
    return res;
}
int suf(int k)
{
    split(rt,k,x,y);
    int res=val[QueryK(y,1)];
    rt=merge(x,y);
    return res;
}
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int op,x;
        scanf("%d%d",&op,&x);
        switch (op)
        {
        case 1:
            Inser(x);
            break;
        case 2:
            del(x);
            break;
        case 3:
            printf("%d\n",QueryRank(x));
            break;
        case 4:
            printf("%d\n",val[QueryK(rt,x)]);
            break;
        case 5:
            printf("%d\n",pre(x));
            break;
        case 6:
            printf("%d\n",suf(x));
            break;
        }
    }
    return 0;
}

区间操作(文艺的平衡树):

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
using namespace std;
int rt,cnt,x,y,z,n,m;
const int maxn=1e5+10;
int ch[maxn][2];
int rnd[maxn];
int val[maxn];
int f[maxn];
int siz[maxn];
void pushdown(int x)
{
    swap(ch[x][0],ch[x][1]);
    if(ch[x][0]) f[ch[x][0]]^=1;
    if(ch[x][1]) f[ch[x][1]]^=1;
    f[x]=0;
}
void pushup(int x)
{
    siz[x]=siz[ch[x][1]]+siz[ch[x][0]]+1;
}
void split(int now,int k,int &x,int &y)
{
    if(!now)
    {
        x=y=0;
        return ;
    }
    if(f[now]) pushdown(now);
    if(siz[ch[now][0]]<k)
    {
        x=now;
        split(ch[now][1],k-siz[ch[now][0]]-1,ch[now][1],y);
    }
    else
    {
        y=now;
        split(ch[now][0],k,x,ch[now][0]);
    }
    pushup(now);
}
int merge(int a,int b)
{
    if(!a||!b)
    {
        return a+b;
    }
    if(rnd[a]<rnd[b])
    {
        if(f[a]) pushdown(a);
        ch[a][1]=merge(ch[a][1],b);
        pushup(a);
        return a;
    }
    else
    {
        if(f[b]) pushdown(b);
        ch[b][0]=merge(a,ch[b][0]);
        pushup(b);
        return b;
    }
}
int new_node(int x)
{
    val[++cnt]=x;
    rnd[cnt]=rand();
    siz[x]=1;
    return cnt;
}
void Insert(int k)
{
    split(rt,k,x,y);
    rt=merge(merge(x,new_node(k)),y);
}
void print(int k)
{
    if(!k) return ;
    if(f[k])pushdown(k);
    print(ch[k][0]);
    cout<<val[k]<<" ";
    print(ch[k][1]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        //int x;
        //scanf("%d",&x);
        rt=merge(rt,new_node(i));
    }
    int l,r;
    while(m--)
    {
        scanf("%d%d",&l,&r);
        split(rt,l-1,x,y);
        split(y,r-l+1,y,z);
        f[y]=1;
        rt=merge(x,merge(y,z));
    }
    print(rt);
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值