BZOJ 3196 二逼平衡树 树套树

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可能为负数

解法:树套树SB题



//二逼平衡树

#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010;
const int maxm = 3000010;
const int inf = 1e8;
int n, m, sz, tmp, a[maxn];
int ls[maxm], rs[maxm], rnd[maxm], v[maxm], s[maxm], w[maxm];
int root[maxn];
void update(int k){
    s[k]=s[ls[k]]+s[rs[k]]+w[k];
}
void rturn(int &k){
    int t=ls[k];
    ls[k]=rs[t];
    rs[t]=k;
    s[t]=s[k];
    update(k);
    k=t;
}
void lturn(int &k){
    int t=rs[k];
    rs[k]=ls[t];
    ls[t]=k;
    s[t]=s[k];
    update(k);
    k=t;
}
void insert(int &k, int x){
    if(k==0){
        sz++;
        k=sz;
        s[k]=w[k]=1;
        v[k]=x;
        rnd[k]=rand();
        return;
    }
    s[k]++;
    if(v[k]==x) w[k]++;
    else if(x>v[k]){
        insert(rs[k],x);
        if(rnd[rs[k]]<rnd[k]) lturn(k);
    }
    else{
        insert(ls[k],x);
        if(rnd[ls[k]]<rnd[k]) rturn(k);
    }
}
void del(int &k, int x){
    if(v[k] == x){
        if(w[k]>1){
            w[k]--;
            s[k]--;
            return;
        }
        if(ls[k]*rs[k]==0) k=ls[k]+rs[k];
        else if(rnd[ls[k]]<rnd[rs[k]]){
            rturn(k);
            del(k,x);
        }
        else{
            lturn(k);
            del(k,x);
        }
    }
    else if(x>v[k]){
        del(rs[k],x);
        s[k]--;
    }
    else{
        del(ls[k],x);
        s[k]--;
    }
}
void build(int k, int l, int r, int x, int num){
    insert(root[k], num);
    if(l == r) return;
    int mid = (l+r)/2;
    if(x<=mid) build(k<<1,l,mid,x,num);
    else build(k<<1|1,mid+1,r,x,num);
}
void query_rank(int k, int x){
    if(k == 0) return;
    if(v[k] == x) {tmp += s[ls[k]]; return;}
    else if(x>v[k]){
        tmp += s[ls[k]]+w[k];
        query_rank(rs[k],x);
    }
    else{
        query_rank(ls[k], x);
    }
}
void query_rank(int k, int l, int r, int x, int y, int num){
    if(l==x&&y==r){
        query_rank(root[k], num);
        return;
    }
    int mid=(l+r)/2;
    if(y<=mid) query_rank(k<<1,l,mid,x,y,num);
    else if(x>mid) query_rank(k<<1|1,mid+1,r,x,y,num);
    else{
        query_rank(k<<1,l,mid,x,mid,num);
        query_rank(k<<1|1,mid+1,r,mid+1,y,num);
    }
}
void getid(int x, int y, int z){
    int l=0,r=inf,ans;
    while(l<=r){
        int mid=(l+r)/2;
        tmp=1;
        query_rank(1,1,n,x,y,mid);
        if(tmp<=z){
            ans=mid;
            l=mid+1;
        }
        else{
            r=mid-1;
        }
    }
    printf("%d\n", ans);
}
void del(int k, int l, int r, int x, int num, int y){
    del(root[k], y);
    insert(root[k], num);
    if(l == r) return;
    int mid=(l+r)/2;
    if(x<=mid) del(k<<1,l,mid,x,num,y);
    else del(k<<1|1,mid+1,r,x,num,y);
}
void query_pro(int k, int x){
    if(!k) return;
    if(v[k]<x){tmp=max(v[k],tmp);query_pro(rs[k],x);}
    else query_pro(ls[k],x);
}
void query_sub(int k, int x){
    if(!k) return;
    if(v[k]>x){tmp=min(tmp,v[k]); query_sub(ls[k],x);}
    else query_sub(rs[k],x);
}
void query_pro(int k, int l, int r, int x, int y, int num){
    if(l==x&&y==r){
        query_pro(root[k], num);
        return;
    }
    int mid=(l+r)/2;
    if(y<=mid) query_pro(k<<1,l,mid,x,y,num);
    else if(x>mid) query_pro(k<<1|1,mid+1,r,x,y,num);
    else{
        query_pro(k<<1,l,mid,x,mid,num);
        query_pro(k<<1|1,mid+1,r,mid+1,y,num);
    }
}
void query_sub(int k, int l, int r, int x, int y, int num){
    if(l==x&&y==r){
        query_sub(root[k], num);
        return;
    }
    int mid=(l+r)/2;
    if(y<=mid) query_sub(k<<1,l,mid,x,y,num);
    else if(x>mid) query_sub(k<<1|1,mid+1,r,x,y,num);
    else{
        query_sub(k<<1,l,mid,x,mid,num);
        query_sub(k<<1|1,mid+1,r,mid+1,y,num);
    }
}
int main()
{
    scanf("%d %d", &n,&m);
    for(int i=1; i<=n; i++) scanf("%d", &a[i]);
    for(int i=1; i<=n; i++) build(1,1,n,i,a[i]);
    for(int i=1; i<=m; i++){
        int op,x,y,k;
        scanf("%d",&op);
        if(op==1){
            scanf("%d %d %d",&x,&y,&k);
            tmp=1;
            query_rank(1,1,n,x,y,k);
            printf("%d\n", tmp);
        }
        else if(op == 2){
            scanf("%d %d %d", &x,&y,&k);
            getid(x,y,k);
        }
        else if(op == 3){
            scanf("%d %d", &x,&y);
            del(1,1,n,x,y,a[x]);
            a[x]=y;
        }
        else if(op == 4){
            scanf("%d %d %d", &x,&y,&k);
            tmp = 0;
            query_pro(1,1,n,x,y,k);
            printf("%d\n", tmp);
        }
        else{
            scanf("%d %d %d", &x,&y,&k);
            tmp = inf;
            query_sub(1,1,n,x,y,k);
            printf("%d\n", tmp);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值