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\n",Sum(RT,-INF,INF,-INF,x-1)+1);
        }
        if(opt==4)
        {
            printf("%d\n",Get(RT,-INF,INF,x));
        }
        if(opt==5)
        {
            int tmp=Sum(RT,-INF,INF,-INF,x-1)+1;//rank x 
            printf("%d\n",Get(RT,-INF,INF,tmp-1));
        }
        if(opt==6)
        {
            int tmp=Sum(RT,-INF,INF,-INF,x)+1;//rank x+1
            printf("%d\n",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

 

posted @ 2019-08-13 11:00  DreamingBligo_Tido  阅读(197)  评论(0编辑  收藏  举报