【题】【线段树(动态开点&负点)】NKOJ 1922 第K小数

NKOJ 1922 第K小数
时间限制 : 150000 MS 空间限制 : 1655360 KB

问题描述
现在已有N个整数,你有以下三种操作:
A 表示加入一个值为A的整数
B 表示删除其中值为B的整数
K 表示输出这些整数中第K小的数

输入格式
第一行,两个整数N,M,表示最开始有N个整数,总共有M个操作
第二行用空格隔开的N个整数
接下来M行,每行表示一个操作

输出格式
若干行,一行一个整数,表示所求的第K小的数字

样例输入
5 5
6 2 7 4 9
1 8
1 6
3 10
2 4
3 3

样例输出
0
7

提示
注意:如果有多个大小相同的数字,只把他们看做一个数字,如样例。
若找不到第K小的数,输出0
数据范围:
0<=N<=2,000,000
M<=1,000,000
-1,000,000,000<=每个整数<=1,000,000,000

思路:
范围太大,动态建立线段树:每次加入一个数,就将线段树对应叶节点的值改为1,若无该节点就建立;每次减少,对应叶节点改为0;每次查找,先查找左儿子是否有k个数,不足说明该找右儿子。
注意先判断是否存在左右儿子

#include<cstdio>
#include<iostream>
using namespace std;

struct tree{int a,b,val;tree *le,*ri;};

tree NBHB,*temp;
int k;
bool mark=false;
//...................................................................
inline void in_(int &d)
{
    bool mark=false;char t=getchar();
    while(t<'0'||t>'9'){if(t=='-') mark=true;t=getchar();}
    for(d=0;t>='0'&&t<='9';t=getchar())d=(d<<3)+(d<<1)-'0'+t;
    if(mark)d=-d;
}  
char o[100];
inline void out_(int x)
{   
    int l=1;
    if(x<0) x=-x,putchar('-');  
    if(!x) putchar('0');  
    for(;x;x/=10) o[l++]=x%10+'0';  
    for(l--;l;l--)putchar(o[l]);  
    putchar('\n');  
}  
//...................................................................
void add(tree *t)
{
    if(t->a==t->b) 
    {
        t->val=1;
        return ;
    }
    int mid=t->a+t->b,x=t->a,y=t->b;
    mid=mid<0?mid/2-1:mid>>1; 
    if(k<=mid)
    {
        if(t->le==NULL) 
        {
            t->le=new tree;
            t->le->a=x,t->le->b=mid,t->le->le=t->le->ri=NULL;
        }
        add(t->le);
    }
    if(k>mid)
    {
        if(t->ri==NULL) 
        {
            t->ri=new tree;
            t->ri->a=mid+1,t->ri->b=y,t->ri->le=t->ri->ri=NULL;
        }
        add(t->ri);
    }
    t->val=0;
    if(t->le!=NULL) t->val+=t->le->val;
    if(t->ri!=NULL) t->val+=t->ri->val;
}
void del(tree *t)
{
    if(mark) return ;
    if(t->a==t->b) 
    {
        t->val=0;
        return ;
    }
    int mid=t->a+t->b,x=t->a,y=t->b;
    mid=mid<0?mid/2-1:mid>>1; 
    if(k<=mid)
    {
        if(t->le==NULL) 
        {
            mark=true;
            return;
        }
        del(t->le);
    }
    if(k>mid)
    {
        if(t->ri==NULL) 
        {
            mark=true;
            return;
        }
        del(t->ri);
    }
    t->val=0;
    if(t->le!=NULL) t->val+=t->le->val;
    if(t->ri!=NULL) t->val+=t->ri->val;
}

int find(tree *t,int k)
{
    if(t->val<k) return 0;
    if((*t).a==(*t).b) return t->b;
    if(t->le!=NULL)
    {
        temp=t->le;
        if(temp->val>=k) return find(temp,k);
        else return find(t->ri,k-temp->val);
    }
    else return find(t->ri,k);
}
//...................................................................
int main()
{
    NBHB.a=-1000000000,NBHB.b=1000000000,NBHB.le=NULL,NBHB.ri=NULL;
    int n,m;in_(n),in_(m);
    for(int i=1;i<=n;i++) 
    {
        in_(k);
        add(&NBHB);
    } 
    for(int i=1,c;i<=m;i++) 
    {
        in_(c),in_(k);
        if(c==1) add(&NBHB);
        else if(c==2) 
        {
            mark=false;
            del(&NBHB);
        }
        else out_(find(&NBHB,k));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值