P3369 【模板】普通平衡树

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入 x x x
  2. 删除 x x x 数(若有多个相同的数,因只删除一个)
  3. 查询 x x x 数的排名(排名定义为比当前数小的数的个数 + 1 +1 +1 )
  4. 查询排名为 x x x 的数
  5. x x x 的前驱(前驱定义为小于 x x x,且最大的数)
  6. x x x 的后继(后继定义为大于 x x x,且最小的数)
    输入格式

第一行为 n n n,表示操作的个数,下面 n n n 行每行有两个数 o p t opt opt x x x o p t opt opt 表示操作的序号 1 ≤ o p t ≤ 6 1≤opt≤6 1opt6 )

输出格式

对于操作 3 , 4 , 5 , 6 3,4,5,6 3,4,5,6 每行输出一个数,表示对应答案


题解:

这道题我用的权值线段树,主要是因为还不会平衡树

我们先把除了操作4的所有数离散化处理一下

对于1,2两个操作,直接在权值线段树上+1,-1即可

对于操作3:查询x数的排名,我们可以查询[1,x-1]区间中数字出现的个数+1即可

对于操作4:查询第k小的数,利用权值线段树的性质,根据出现数的个数选择递归左儿子还是右儿子即可

对于操作5:求x的前驱,求出[1,x-1]区间内的数字出现次数,然后这个区间内最靠近x的数就是x的前驱

对于操作6:求x的后驱,求出[1,x]区间内的数字出现次数,然后这个区间后面的第一个数就是x的后驱


AC代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+50;
struct node{ int l,r,sum; }t[MAXN<<2];
int a[MAXN],op[MAXN]; vector<int> v;
inline void build(int rt,int l,int r){
    t[rt].l=l,t[rt].r=r,t[rt].sum=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(rt<<1,l,mid); build(rt<<1|1,mid+1,r);
}
inline void update(int rt,int k,int w){//操作1,2
    if(t[rt].l==t[rt].r) { t[rt].sum+=w; return; }
    int mid=(t[rt].l+t[rt].r)>>1;
    if(k<=mid) update(rt<<1,k,w);
    else update(rt<<1|1,k,w);
    t[rt].sum = t[rt<<1].sum+t[rt<<1|1].sum;
}
inline int query(int rt,int ql,int qr){//查询ql到qr区间中数字的出现次数
    if(ql>qr) return 0;
    if(ql<=t[rt].l && t[rt].r<=qr) return t[rt].sum;
    int mid=(t[rt].l+t[rt].r)>>1;
    if(qr<=mid) return query(rt<<1,ql,qr);
    else if(ql>mid) return query(rt<<1|1,ql,qr);
    else return query(rt<<1,ql,mid)+query(rt<<1|1,mid+1,qr);
}
inline int kth(int rt,int k){//区间第k小
    if(t[rt].l==t[rt].r) return t[rt].l;
    int mid=(t[rt].l+t[rt].r)>>1;
    if(t[rt<<1].sum>=k) return kth(rt<<1,k);
    else return kth(rt<<1|1,k-t[rt<<1].sum);
}
inline int Id(int x){ return lower_bound(v.begin(),v.end(),x)-v.begin()+1; }
int main(){
    //freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
    int n; scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&op[i],&a[i]);
        if(op[i]==4) continue;
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    int m=v.size();
    build(1,1,m);
    for(int i=1;i<=n;i++){
        if(op[i]==1) update(1,Id(a[i]),1);
        else if(op[i]==2) update(1,Id(a[i]),-1);
        else if(op[i]==3) printf("%d\n",query(1,1,Id(a[i])-1)+1);
        else if(op[i]==4) printf("%d\n",v[kth(1,a[i])-1]);
        else if(op[i]==5) printf("%d\n",v[kth(1,query(1,1,Id(a[i])-1))-1]);
        else if(op[i]==6) printf("%d\n",v[kth(1,query(1,1,Id(a[i]))+1)-1]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值