BZOJ3196二逼平衡树——树套树

Description

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

Input

第一行两个数 n,m 表示长度为n的有序序列和m个操作
第二行有n个数,表示有序序列
下面有m行,opt表示操作标号
若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继

Output

对于操作1,2,4,5各输出一行,表示查询结果

Sample Input

9 6

4 2 2 1 9 4 0 1 1

2 1 4 3

3 4 10

2 1 4 3

1 2 5 9

4 3 9 5

5 2 8 5
Sample Output

2

4

3

4

9
HINT

1.n和m的数据范围:n,m<=50000

2.序列中每个数的数据范围:[0,1e8]

3.虽然原题没有,但事实上5操作的k可能为负数


由于这题既要维护区间的信息,又要搞排名,前驱,后继等平衡树的操作。所以这题的思路就是线段树套平衡树(树套树)
我们对于线段树的每一个区间都建立一个平衡树,那么我们就可以进行操作了。我们一个一个来看:
操作1:我们查询线段树的区间,将每一小块区间里小于x的数累加,再加1(x自己)即可。
操作3:为什么不先操作2?因为操作2是所有操作里最繁琐的,就在线段树里一直递归到这个点,然后在途经的所有平衡树中删去这个点的权值,再加进x的值即可。
操作4:在线段树里每一个小块找x的前驱,最后取个max。
操作5:在线段树里每一个小块找x的后继,最后取min。
现在我们来讲操作2:直接二分答案,二分一个权值,然后查询区间里有的排名,如果刚好等于k,那么就让r=mid,这样可以保证最终的点取到所求的x的权值vx(因为vx-1的排名肯定是x的排名-1)
int GetPl(int l,int r,int k){
    int cl=0,cr=MX+1,mid;
    while(cl<cr){
        mid=(cl+cr)>>1;
        dist=0;SegRank(1,1,n,l,r,mid);
        if(dist<k) cl=mid+1;
        else cr=mid;
    }
    return cl-1;
}
Code:
#include<bits/stdc++.h>
#define MAXN 3000005
#define INF 2147483647
using namespace std;
int read(){
    char c;int x=0,y=1;while(c=getchar(),(c<'0'||c>'9')&&c!='-');
    if(c=='-') y=-1;else x=c-'0';while(c=getchar(),c>='0'&&c<='9')
    x=x*10+c-'0';return x*y;
}
int n,m,MX,dist,tot,a[MAXN],rt[MAXN];
int son[MAXN][2],fa[MAXN],val[MAXN],siz[MAXN],ct[MAXN];
void clear(int k){
    fa[k]=son[k][1]=son[k][0]=val[k]=siz[k]=ct[k]=0;
}
void pushup(int k){  //统计子树大小
    siz[k]=(son[k][0]?siz[son[k][0]]:0)+(son[k][1]?siz[son[k][1]]:0)+ct[k];
}
void rotate(int k){  
    int f=fa[k],gran=fa[f],d=son[f][1]==k,w=son[k][d^1];
    son[f][d]=w;fa[f]=k;son[k][d^1]=f;if(w) fa[w]=f;
    if(gran) son[gran][son[gran][1]==f]=k;fa[k]=gran;
    pushup(f);pushup(k);
}
void splay(int i,int k,int top){   //splay操作
    while(fa[k]!=top){
        int f=fa[k],gran=fa[f];
        if(gran!=top) rotate((son[f][1]==k)^(son[gran][1]==f)?k:f);
        rotate(k);
    }
    if(!top) rt[i]=k;
}
void SplayInsert(int p,int x){  //在splay里添加点
    int wh=rt[p];
    if(!rt[p]){
        rt[p]=wh=++tot;fa[wh]=0; 
        val[wh]=x;siz[wh]=ct[wh]=1;
        son[wh][0]=son[wh][1]=0;
        return;
    }
    int last=0;
    while(1){
        if(val[wh]==x){ct[wh]++;pushup(wh);break;}
        last=wh;wh=son[wh][x>val[wh]];
        if(!wh){
            wh=++tot;val[wh]=x;son[last][x>val[last]]=wh;
            son[wh][0]=son[wh][1]=0;
            fa[wh]=last;siz[wh]=ct[wh]=1;
            pushup(last);break;
        }
    }
    splay(p,wh,0);
}
int SplayRank(int p,int k){   //查询在splay里的排名
    int wh=rt[p],ret=0;
    while(wh){
        if(val[wh]>k) wh=son[wh][0];
        else{
            if(val[wh]<k){
                ret+=(son[wh][0]?siz[son[wh][0]]:0)+ct[wh];wh=son[wh][1];
            }
            else if(val[wh]==k) return ret+(son[wh][0]?siz[son[wh][0]]:0);
        }
    }
    return ret;
}
int access(int p,int x){    //将权值为x的点变为splay的根
    int wh=rt[p];
    while(wh){
        if(val[wh]==x){
            splay(p,wh,0);return wh;
        }
        wh=son[wh][x>val[wh]];
    }
}
int pre(int p){int x=son[rt[p]][0];while(son[x][1])x=son[x][1];return x;}
void SplayDelete(int p,int elemental){    //splay删点
    int x=access(p,elemental);
    if(ct[x]>1){ct[x]--;pushup(x);return;}
    if(!son[x][0]&&!son[x][1]){clear(x);rt[p]=0;return;}
    if(!son[x][0]){
        int y=son[x][1];rt[p]=y;fa[y]=0;
        return;
    }
    if(!son[x][1]){
        int y=son[x][0];rt[p]=y;fa[y]=0;
        return;
    }
    int wh=pre(p),oldrt=rt[p];
    splay(p,wh,0);
    son[rt[p]][1]=son[oldrt][1];fa[son[oldrt][1]]=rt[p];
    clear(oldrt);pushup(rt[p]);
}
int GetPre(int p,int x){   //splay里找前驱
    int wh=rt[p];
    while(wh){
        if(val[wh]<x){
            dist=max(dist,val[wh]);
            wh=son[wh][1];
        }
        else wh=son[wh][0];
    }
    return dist;
}
int GetSuff(int p,int x){   //splay里找后继
    int wh=rt[p];
    while(wh){
        if(val[wh]>x){
            dist=min(dist,val[wh]);
            wh=son[wh][0];
        }
        else wh=son[wh][1];
    }
    return dist;
}
void SegInsert(int k,int l,int r,int p,int x){   //线段树差点
    SplayInsert(k,x);
    if(l==r) return;
    int mid=(l+r)>>1;
    if(p<=mid) SegInsert(k<<1,l,mid,p,x);
    else SegInsert(k<<1|1,mid+1,r,p,x);
}
void SegRank(int k,int l,int r,int L,int R,int x){   //查询区间排名
    if(L<=l&&r<=R){
        dist+=SplayRank(k,x);return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) SegRank(k<<1,l,mid,L,R,x);
    if(R>mid) SegRank(k<<1|1,mid+1,r,L,R,x);
}
void SegTransform(int k,int l,int r,int p,int x){   //改某个点的权值
    SplayDelete(k,a[p]);SplayInsert(k,x);
    if(l==r){a[p]=x;return;}
    int mid=(l+r)>>1;
    if(p<=mid) SegTransform(k<<1,l,mid,p,x);
    else SegTransform(k<<1|1,mid+1,r,p,x);
}
void SegPre(int k,int l,int r,int L,int R,int x){    //查询区间前驱
    if(L<=l&&r<=R){
        dist=max(dist,GetPre(k,x));
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) SegPre(k<<1,l,mid,L,R,x);
    if(R>mid) SegPre(k<<1|1,mid+1,r,L,R,x);
}
void SegSuff(int k,int l,int r,int L,int R,int x){     //查询区间后继
    if(L<=l&&r<=R){
        dist=min(dist,GetSuff(k,x));
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) SegSuff(k<<1,l,mid,L,R,x);
    if(R>mid) SegSuff(k<<1|1,mid+1,r,L,R,x);
}
int GetPl(int l,int r,int k){    //查询排名为x的点
    int cl=0,cr=MX+1,mid;
    while(cl<cr){
        mid=(cl+cr)>>1;
        dist=0;SegRank(1,1,n,l,r,mid);
        if(dist<k) cl=mid+1;
        else cr=mid;
    }
    return cl-1;
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        SegInsert(1,1,n,i,a[i]);
        MX=max(MX,a[i]);
    }
    for(int i=1;i<=m;i++){
        int type=read();
        if(type==1){
            int l=read(),r=read(),k=read();dist=0;
            SegRank(1,1,n,l,r,k);printf("%d\n",dist+1);
        }
        if(type==2){
            int l=read(),r=read(),k=read();
            printf("%d\n",GetPl(l,r,k));
        }
        if(type==3){
            int p=read(),x=read();
            SegTransform(1,1,n,p,x);
        }
        if(type==4){
            int l=read(),r=read(),k=read();dist=-INF;
            SegPre(1,1,n,l,r,k);printf("%d\n",dist);
        }
        if(type==5){
            int l=read(),r=read(),k=read();dist=INF;
            SegSuff(1,1,n,l,r,k);printf("%d\n",dist);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值