洛谷P3369 普通平衡树 - 权值线段树解法

题目描述

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

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

输入输出格式

输入格式:

第一行为nnn,表示操作的个数,下面nnn行每行有两个数optoptopt和xxx,optoptopt表示操作的序号( 1≤opt≤6 1 \leq opt \leq 6 1≤opt≤6 )

输出格式:

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

输入输出样例

输入样例#1: 复制

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

输出样例#1: 复制

106465
84185
492737

说明

时空限制:1000ms,128M

1.n的数据范围: n≤100000 n \leq 100000 n≤100000

2.每个数的数据范围: [−107,107][-{10}^7, {10}^7][−107,107]

来源:Tyvj1728 原名:普通平衡树

思路:

用权值线段树写,注意要先离散化

(3)求x的排名,就是求[1,x-1]的区间sum值+1

(5)求x的前驱,就是sum(1,x-1)求出1到x-1的所有数的个数,再求排名为sum(1,x-1)的数

(6)求x的后继,就是sum(1,x)求出1到x的所有数的个数,再求排名为sum(1,x)+1的数

坑点:

(1)题目中会问你一个从来没插入的数的前驱/后继,所以存数的时候都要先存下来,再离散化

(2)问你数x的排名时,可能数x是最小的那个数,那么就不能sum(1,x-1)了,因为x对应的值是1,sum(1,0)(左端点大于有端点)会导致MLE,特判一下当x是最小的数时,输出1就可

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=100005;
int num[N];
struct A{
    int opt,x;
}q[N];
int tree[N<<2];

void push_up(int rt){
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

void update(int p,int c,int l,int r,int rt){
    if(l==r){
        tree[rt]+=c;
        return ;
    }
    int m=(l+r)>>1;
    if(p<=m)update(p,c,lson);
    else update(p,c,rson);
    push_up(rt);
}

int query1(int L,int R,int l,int r,int rt){//区间求和
    if(L<=l&&R>=r){
        return tree[rt];
    }
    int m=(l+r)>>1;
    int ans=0;
    if(L<=m)ans+=query1(L,R,lson);
    if(R>m)ans+=query1(L,R,rson);
    return ans;
}

int query2(int k,int l,int r,int rt){//查询排名为k的数
    if(l==r){
        return l;
    }
    int m=(l+r)>>1;
    if(k<=tree[rt<<1])return query2(k,lson);
    else return query2(k-tree[rt<<1],rson);
}


int main(){
    int m,k=0;
    scanf("%d",&m);
    for(int i=0;i<m;i++){
        scanf("%d%d",&q[i].opt,&q[i].x);
        if(q[i].opt!=4)num[k++]=q[i].x;
    }
    sort(num,num+k);
    int n=unique(num,num+k)-num;

    for(int i=0;i<m;i++){
        int x=lower_bound(num,num+n,q[i].x)-num+1;
        if(q[i].opt==1){//插入
            update(x,1,1,n,1);
        }
        if(q[i].opt==2){//删除
            update(x,-1,1,n,1);
        }
        if(q[i].opt==3){//查询x的排名
            if(x-1==0)printf("1\n");
            else printf("%d\n",query1(1,x-1,1,n,1)+1);
        }
        if(q[i].opt==4){//查询排名为x的数
            printf("%d\n",num[query2(q[i].x,1,n,1)-1]);
        }
        if(q[i].opt==5){//求小于x的最大的数的值
            int rk=query1(1,x-1,1,n,1);
            printf("%d\n",num[query2(rk,1,n,1)-1]);
        }
        if(q[i].opt==6){//求大于x的最小的数的值
            int sum=query1(1,x,1,n,1);
            printf("%d\n",num[query2(sum+1,1,n,1)-1]);
        }
    }
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值