zoukankan      html  css  js  c++  java
  • cogs 1829. [Tyvj 1728]普通平衡树 权值线段树

     

    1829. [Tyvj 1728]普通平衡树

    ★★★   输入文件:phs.in   输出文件:phs.out   简单对比
    时间限制:1 s   内存限制:1000 MB

    【题目描述】

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
    1. 插入x数
    2. 删除x数(若有多个相同的数,因只删除一个)
    3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x,且最大的数)
    6. 求x的后继(后继定义为大于x,且最小的数)

    【输入格式】

    第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

    【输出格式】

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

    【样例输入】

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

    【样例输出】

    106465
    84185
    492737

    【提示】

    1.n的数据范围:n<=100000

    2.每个数的数据范围:[-1e7,1e7]
     
     
    权值线段树
    动态开点
    先贴一下代码吧
    #include<bits/stdc++.h>
    #define maxn 100005
    #define maxSIZE maxn*20
    #define INF 1e7
    #define mid (l+r>>1)
    using namespace std;
    int sum[maxSIZE],ls[maxSIZE],rs[maxSIZE];
    int RT,cnt;//动态开点 不会炸内存 
    void Add(int &rt,int l,int r,int pos,int qx)
    {
        if(!rt)
            rt=++cnt;
        if(l==r)
        {
            sum[rt]+=qx;
            return;
        }
        if(pos<=mid)
            Add(ls[rt],l,mid,pos,qx);
        else 
            Add(rs[rt],mid+1,r,pos,qx);
        sum[rt]=sum[ls[rt]]+sum[rs[rt]];
        return;
    }
    int Sum(int rt,int l,int r,int s,int t)
    {
        if(!rt||s>r||t<l)
            return 0;
        if(s<=l&&r<=t)
            return sum[rt];
        return Sum(ls[rt],l,mid,s,t)+Sum(rs[rt],mid+1,r,s,t);
    }
    int Get(int rt,int l,int r,int rank)
    {
        if(l==r)//保证合法  不用判rt 
        {
            return l;
        }
        if(rank<=sum[ls[rt]])
            return Get(ls[rt],l,mid,rank);
        else
            return Get(rs[rt],mid+1,r,rank-sum[ls[rt]]);
    }
    int main()
    {
        freopen("phs.in","r",stdin);
        freopen("phs.out","w",stdout);
        int T;
        scanf("%d",&T);
        while(T--)
        {
            int opt,x;
            scanf("%d%d",&opt,&x);
            if(opt==1)
            {
                Add(RT,-INF,INF,x,1);
            }
            if(opt==2)
            {
                Add(RT,-INF,INF,x,-1);
            }
            if(opt==3)
            {
                printf("%d
    ",Sum(RT,-INF,INF,-INF,x-1)+1);
            }
            if(opt==4)
            {
                printf("%d
    ",Get(RT,-INF,INF,x));
            }
            if(opt==5)
            {
                int tmp=Sum(RT,-INF,INF,-INF,x-1)+1;//rank x 
                printf("%d
    ",Get(RT,-INF,INF,tmp-1));
            }
            if(opt==6)
            {
                int tmp=Sum(RT,-INF,INF,-INF,x)+1;//rank x+1
                printf("%d
    ",Get(RT,-INF,INF,tmp)); 
            }
        }
        
        return 0;
    }
    我的代码

    那么这一大堆东西到底是什么意思呢?

    权值线段树 就是线段树里不是下标 是值域

    然后我们这里有几个操作

    操作一 就是插入操作嘛

    就把那个数值的个数+1

    操作二 删除操作 

    一样的 就是数值的个数-1

    操作三 查询x数的排名

    就是查找值域范围从-INF到x-1内一共有多少个数 再加上一个1就行了

    操作四 查询排名为x的数是什么

    可以写一个Get函数来进行一下二分

      这一段到底什么意思

    就是说如果在左儿子里 就往左走

    要是在右儿子里 不仅要往右走 还有要 rank-左边一共有多少个数 因为左边的那些数都比当给钱要查找的这个排名是rank的数要小

    操作五 求小于x的最大的数

    这个操作吗 就是查找x个排名-1的数就行了

    操作六 求大于x的最小的数

    我们可以加一个数

    求一下x+1的排名  在查找一下那个排名的位置的数值是什么

    听起来非常玄学 就是我们可以加一个x+1这样一个虚拟的数(存不存在不要紧) 只是先查出来它的排名而已

    最后一点非常重要 要动态开点 不要用p*2 和p*2+1的那种存储方式 会炸内存的

    一共只需要开log(len)*n的数组大小就行了

    len就是值域总范围 在这一道题中是2e7

  • 相关阅读:
    java 的三种代理模式 (二)——子函数切面
    王者荣耀为什么不使用微服务架构,服务的极简主义,为什么交易网关使用redis做持久
    tcp_syncookies 半连接
    tcp_tw_recycle tcp_tw_reuse与timewait【yetdone】
    动态代理,没有被代理对象
    一次jstack解决update停顿
    动态代理反向
    注解的继承
    51单片机状态机键盘检测
    28335scififo中断接收与发送
  • 原文地址:https://www.cnblogs.com/Tidoblogs/p/11344575.html
Copyright © 2011-2022 走看看