平衡树三题:bzoj3224+bzoj3223+bzoj3196 普通平衡树 文艺平衡树 二逼平衡树(splay)

本文通过三个具体的题目,介绍了如何使用平衡树解决复杂的数据结构问题。包括普通平衡树、文艺平衡树和二逼平衡树的实现与应用。

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

bzoj3224 Tyvj 1728 普通平衡树

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=3224

题意:
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

数据范围
1.n的数据范围:n<=100000

2.每个数的数据范围:[-2e9,2e9]

题解:
基础平衡树操作。
旧代码。

代码:


#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100005;
int n;
int root=0;
int fa[N],ch[N][2],size[N],cnt[N],key[N];
int sz=0;
inline void update(int k)
{
    if(k)
    {   
        size[k]=cnt[k];
        if(ch[k][0]) size[k]+=size[ch[k][0]];
        if(ch[k][1]) size[k]+=size[ch[k][1]];
    }
    return;
}
inline bool get(int x){return ch[fa[x]][1]==x;}
inline void rotate(int x,int &k)
{
    int y=fa[x],z=fa[y];
    int l,r;
    if(ch[y][0]==x) l=0;else l=1;r=l^1;
    if(y==k) k=x;
    else
    {
        if(ch[z][0]==y) ch[z][0]=x; else ch[z][1]=x;
    }
    ch[y][l]=ch[x][r]; fa[ch[x][r]]=y;
    ch[x][r]=y; fa[x]=z; fa[y]=x;
    update(y);update(x);
    return;
}
inline void splay(int x,int &k)
{
    while(x!=k)
    {
        int y=fa[x],z=fa[y];
        if(y!=k)
        {
            if(x==ch[y][0]^y==ch[z][0]) rotate(x,k);
            else rotate(y,k);
        }
        rotate(x,k);
    }
    return;
}

inline int findx(int x)
{
    int now=root;
    while(1)
    {
        if(ch[now][0]&&x<=size[ch[now][0]]) now=ch[now][0];
        else
        {
            int tmp=cnt[now]+(ch[now][0]?size[ch[now][0]]:0);
            if(x<=tmp) return key[now];
            x-=tmp; now=ch[now][1]; 
        }
    }
}
inline void insert(int x)
{
    if(root==0) { sz++;size[sz]=1;ch[sz][0]=ch[sz][1]=0;root=sz;cnt[sz]=1;fa[sz]=0;key[sz]=x; return; }
    int now=root,f=0;
    while(1)
    {

        if(key[now]==x)
        {size[now]++;cnt[now]++;update(f);splay(now,root); break;} 
        if(x<key[now]) { f=now;now=ch[now][0];}
        else{ f=now; now=ch[now][1];}
        if(now==0)
        {
            sz++;now=sz;size[sz]=1;cnt[sz]=1;ch[sz][0]=ch[sz][1]=0;fa[sz]=f;key[sz]=x;
            if(x<key[f]) ch[f][0]=sz; else ch[f][1]=sz;
            update(f); splay(now,root);break;
        }  
    }
    return;
}
inline int find(int x)
{
    int now=root,ans=0;
    while(1)
    {
        if(x<key[now]) now=ch[now][0];
        else
        {
            ans+=(ch[now][0]?size[ch[now][0]]:0);
            if(key[now]==x){ splay(now,root); return ans+1;}
            ans+=cnt[now]; now=ch[now][1];
        }         
    }
}
inline void clear(int k)
{
    ch[k][0]=ch[k][1]=key[k]=cnt[k]=size[k]=fa[k]=0;return;
}
inline int getpre()
{
    int now=ch[root][0];
    while(ch[now][1]) now=ch[now][1];
    return now;
}
inline int getnxt()
{
    int now=ch[root][1];
    while(ch[now][0]) now=ch[now][0];
    return now;
}
inline void de(int x)
{
    int now=root,f=0;
    if(root==0) return;
    int sss=find(x);
    if(cnt[root]>1){cnt[root]--; update(root);return;}     
    if(!ch[root][0]&&!ch[root][1]) {clear(root); root=0;return;} //注意改根清空。
    if(!ch[root][0]) {int oldroot=root;root=ch[oldroot][1]; fa[root]=0; clear(oldroot);return;}
    if(!ch[root][1]) {int oldroot=root;root=ch[oldroot][0]; fa[root]=0; clear(oldroot);return;}
    else
    {
        int pre=getpre();
        int oldroot=root;
        splay(pre,root);
        fa[ch[oldroot][1]]=root; ch[root][1]=ch[oldroot][1];
        clear(oldroot);update(root);
        return;
    }          
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        switch(a){

            case 1: insert(b); break;
            case 2: de(b); break;
            case 3: printf("%d\n",find(b)); break;
            case 4: printf("%d\n",findx(b)); break;
            case 5: {
                insert(b);
                printf("%d\n",key[getpre()]);
                de(b);
                break;
            }
            case 6: {
                insert(b);
                printf("%d\n",key[getnxt()]);
                de(b);
                break;
            }
        }
    }
    return 0;
}

bzoj3223 Tyvj 1729 文艺平衡树

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=3223

题意:
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

数据范围
N,M<=100000

题解:
基础splay操作。
旧代码。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=100005;
int fa[N],ch[N][2],id[N],size[N];
int n,m,root;
bool rev[N];
void update(int k)
{
    size[k]=size[ch[k][0]]+size[ch[k][1]]+1;
}
void build(int l,int r,int f)
{
    if(l>r) return;
    int now=id[l];int last=id[f];
    if(l==r)
    {
        size[now]=1;rev[now]=0;fa[now]=last;
        if(l<f) ch[last][0]=now;
        else ch[last][1]=now;
        return;
    }
    else
    {
        int mid=(l+r)>>1;
        now=id[mid];
        build(l,mid-1,mid);build(mid+1,r,mid);
        rev[now]=0; fa[now]=last;
        if(mid<f) ch[last][0]=now;
        else ch[last][1]=now;
        update(now);
    }
}
void pushdown(int k)
{    
    if(rev[k])
    {
        swap(ch[k][0],ch[k][1]);
        rev[ch[k][0]]^=1;
        rev[ch[k][1]]^=1;
        rev[k]=0;
    }
}
void rotate(int x,int &k)
{
    int y=fa[x]; int z=fa[y];
    int l,r;
    if(ch[y][0]==x) l=0; else l=1; r=l^1;
    if(y==k) k=x;
    else
    {if(ch[z][0]==y) ch[z][0]=x; else ch[z][1]=x;}
    fa[x]=z; fa[y]=x; fa[ch[x][r]]=y;
     ch[y][l]=ch[x][r];ch[x][r]=y;
    update(y);update(x);
}
void splay(int x,int &k) //传地址的意义 在于直接改变root 
{     
    while(x!=k)
    {
        int y=fa[x],z=fa[y];
        if(y!=k)
        {
            if(ch[y][0]==x^ch[z][0]==y)
            rotate(x,k);
            else rotate(y,k);
        }        
        rotate(x,k);
    }
}
int find(int k,int rank)
{
    pushdown(k);
    int l=ch[k][0];int r=ch[k][1];
    if(size[l]+1==rank) return k;
    if(size[l]>=rank) return find(l,rank);
    else
    return find(r,rank-size[l]-1);    
}
void rever(int l,int r)
{

    int x=find(root,l);int y=find(root,r+2);
    splay(x,root);
    splay(y,ch[x][1]);  
    int z=ch[y][0];
    rev[z]^=1;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n+2;i++)
    id[i]=i;
    root=(n+3)>>1;    
    build(1,n+2,0);
    int l,r;
    for(int i=1;i<=m;i++)
    {       
        scanf("%d%d",&l,&r);
        rever(l,r);
    }
    for(int i=1;i<=n;i++) printf("%d ",find(root,i+1)-1);
    return 0;
}

bzoj3196 Tyvj 1730 二逼平衡树

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=3196

题意:
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)

数据范围
1.n和m的数据范围:n,m<=50000
2.序列中每个数的数据范围:[0,1e8]
3.虽然原题没有,但事实上5操作的k可能为负数

题解:
线段树套splay(大常数组合orz)
线段树的每一个节点建一个该区间的平衡树。(空间O(nlogn))
opt1:查询覆盖该区间的线段树节点对应平衡树中比k小的个数+1
opt2:二分这个数,转化为opt1
opt3:该点向上一条链的线段树平衡树中都删除一个这个点对应的值,插入一个这个点的新值。
opt4:查询覆盖该区间的线段树节点对应平衡树中该值前驱并取最大。
opt5:查询覆盖该区间的线段树节点对应平衡树中该值后并取最小。
(200行 + 的代码_ (:P」∠)_ )

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=50005;
const int NN=2000005;
const int inf=0x3f3f3f3f;
struct node
{
    int ch[2],fa,val,size,cnt;
    void init()
    {
        ch[0]=ch[1]=fa=val=size=cnt=0;
    }
}tr[NN];
struct NODE
{
    int ls,rs,root;
    void init()
    {
        ls=rs=root=0;
    }
}TR[2*N];
int n,m,tail=0,TAIL=0,a[N],root;
void update(int nd)
{
    int ls=tr[nd].ch[0]; int rs=tr[nd].ch[1];
    tr[nd].size=tr[nd].cnt;
    if(ls) tr[nd].size+=tr[ls].size;
    if(rs) tr[nd].size+=tr[rs].size;
}
int insert(int &nd,int fa,int val)
{
    if(!nd) {nd=++tail; tr[nd].init(); tr[nd].fa=fa; tr[nd].size=tr[nd].cnt=1; tr[nd].val=val; return nd;}
    int pos;
    if(val<tr[nd].val) pos=insert(tr[nd].ch[0],nd,val);
    else if(val>tr[nd].val) pos=insert(tr[nd].ch[1],nd,val);
    else {tr[nd].cnt++; pos=nd;}
    update(nd); 
    return nd;
}
void rotate(int x,int &top)
{
    int y=tr[x].fa; int z=tr[y].fa;
    if(y==top) top=x;
    else  if(tr[z].ch[0]==y) tr[z].ch[0]=x; else tr[z].ch[1]=x;
    tr[x].fa=z;
    int l= (tr[y].ch[0]==x)? 0:1; int r=l^1;
    tr[y].ch[l]=tr[x].ch[r]; tr[tr[x].ch[r]].fa=y;
    tr[x].ch[r]=y; tr[y].fa=x;
    update(y); update(x);
}
void splay(int x,int &top)
{
    while(x!=top)
    {

        int y=tr[x].fa; int z=tr[y].fa;
        if(y!=top)
        {
            if(tr[y].ch[0]==x ^ tr[z].ch[0]==y) rotate(x,top);
            else rotate(y,top);
        }
        rotate(x,top);
    }
}
void build(int &nd,int lf,int rg)
{
    nd=++TAIL;
    for(int i=lf;i<=rg;i++) {int pos=insert(TR[nd].root,0,a[i]); splay(pos,TR[nd].root);} 
    if(lf==rg) return;
    int mid=(lf+rg)>>1;
    build(TR[nd].ls,lf,mid);
    build(TR[nd].rs,mid+1,rg);
}
int query(int nd,int val)
{
    if(!nd) return 0;
    if(tr[nd].val==val) return tr[tr[nd].ch[0]].size;
    else if(tr[nd].val<val) return tr[tr[nd].ch[0]].size+tr[nd].cnt+query(tr[nd].ch[1],val);
    else return query(tr[nd].ch[0],val);
}
int query(int nd,int lf,int rg,int L,int R,int val)
{
    if(L<=lf&&rg<=R) return query(TR[nd].root,val);
    int ans=0;
    int mid=(lf+rg)>>1;
    if(L<=mid) ans+=query(TR[nd].ls,lf,mid,L,R,val);
    if(R>mid) ans+=query(TR[nd].rs,mid+1,rg,L,R,val);
    return ans;
}
int find(int nd,int val)
{
    if(!nd) return 0;
    int ls=tr[nd].ch[0]; int rs=tr[nd].ch[1];
    if(tr[nd].val==val) return nd;
    else if(val<tr[nd].val) return find(tr[nd].ch[0],val);
    else return find(tr[nd].ch[1],val);
}
int getpre(int x)
{
    int tmp=tr[x].ch[0];
    while(tr[tmp].ch[1]) tmp=tr[tmp].ch[1];
    return tmp;
}
void del(int &x)
{
    if(tr[x].cnt>1) {tr[x].cnt--;  update(x); return;}
    if(!tr[x].ch[0]&&!tr[x].ch[1]) { tr[x].init(); x=0; return;}    
    else if(!tr[x].ch[0]||!tr[x].ch[1])
    {
        int l= tr[x].ch[0]==0? tr[x].ch[1]:tr[x].ch[0];
        tr[x].init(); x=l; tr[x].fa=0; update(x); return;
    }
    int now=x; int pre=getpre(now);
    splay(pre,x);
    tr[pre].ch[1]=tr[now].ch[1];
    tr[tr[now].ch[1]].fa=pre;
    tr[now].init();
    update(pre);
    return;
}
void modify(int &nd,int pos,int val)
{
    int pv=a[pos];
    int x=find(nd,pv);
    splay(x,nd); del(nd);
    int now=insert(nd,n,val); splay(now,nd);
}
void modify(int nd,int lf,int rg,int pos,int val)
{
    modify(TR[nd].root,pos,val);
    if(lf==rg) return;
    int mid=(lf+rg)>>1;
    if(pos<=mid) modify(TR[nd].ls,lf,mid,pos,val);
    if(pos>mid) modify(TR[nd].rs,mid+1,rg,pos,val);
}
int preval(int nd,int val)
{
    int tmp=nd; int ans=-inf;
    while(tmp)
    {
        if(tr[tmp].val<val) 
        {
            ans=max(tr[tmp].val,ans);
            tmp=tr[tmp].ch[1];
        }
        else tmp=tr[tmp].ch[0];
    }
    return ans;
}
int nxtval(int nd,int val)
{
    int tmp=nd; int ans=inf;
    while(tmp)
    {
        if(tr[tmp].val>val) 
        {
            ans=min(tr[tmp].val,ans);
            tmp=tr[tmp].ch[0];
        }
        else tmp=tr[tmp].ch[1];
    }
    return ans;
}
int query_pre(int nd,int lf,int rg,int L,int R,int val)
{
    if(L<=lf&&rg<=R) return preval(TR[nd].root,val);
    int ans=-inf;
    int mid=(lf+rg)>>1;
    if(L<=mid) ans=max(ans,query_pre(TR[nd].ls,lf,mid,L,R,val));
    if(R>mid) ans=max(ans,query_pre(TR[nd].rs,mid+1,rg,L,R,val));
    return ans;
}

int query_nxt(int nd,int lf,int rg,int L,int R,int val)
{
    if(L<=lf&&rg<=R) return nxtval(TR[nd].root,val);
    int ans=inf;
    int mid=(lf+rg)>>1;
    if(L<=mid) ans=min(ans,query_nxt(TR[nd].ls,lf,mid,L,R,val));
    if(R>mid) ans=min(ans,query_nxt(TR[nd].rs,mid+1,rg,L,R,val));
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);  
    tr[0].init(); build(root,1,n);
    while(m--)
    {
        int opt,l,r,pos,k; scanf("%d",&opt);
        if(opt==1) //查询k在区间[l,r]内的排名
        {
            scanf("%d%d%d",&l,&r,&k);
            printf("%d\n",query(root,1,n,l,r,k)+1);
        }
        else if(opt==2)//之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
        {
            scanf("%d%d%d",&l,&r,&k);
            int lf=0; int rg=100000000;
            while(lf+1<rg)
            {
                int mid=(lf+rg)>>1;
                if(query(root,1,n,l,r,mid)+1<=k) lf=mid;
                else rg=mid;
            }
            if(query(root,1,n,l,r,rg)+1<=k) printf("%d\n",rg);
            else printf("%d\n",lf);
        }
        else if(opt==3)//之后有两个数pos,k 表示将pos位置的数修改为k
        {
            scanf("%d%d",&pos,&k);
            modify(root,1,n,pos,k);
            a[pos]=k;
        }
        else if(opt==4)
        {
            scanf("%d%d%d",&l,&r,&k); //之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
            printf("%d\n",query_pre(root,1,n,l,r,k));
        }
        else if(opt==5)
        {
            scanf("%d%d%d",&l,&r,&k); //之后有三个数l,r,k 表示查询区间[l,r]内k的后驱
            printf("%d\n",query_nxt(root,1,n,l,r,k));
        }
    }   
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值