P5076 【深基16.例7】普通二叉树(简化版)
题目描述
您需要写一种数据结构,来维护一些数( 都是 10^9109 以内的数字)的集合,最开始时集合是空的。其中需要提供以下操作,操作次数 qq 不超过 10^4104:
- 查询 xx 数的排名(排名定义为比当前数小的数的个数 +1+1。若有多个相同的数,应输出最小的排名)。
- 查询排名为 xx 的数。
- 求 xx 的前驱(前驱定义为小于 xx,且最大的数)。若未找到则输出 -2147483647−2147483647。
- 求 xx 的后继(后继定义为大于 xx,且最小的数)。若未找到则输出 21474836472147483647。
- 插入一个数 xx。
输入格式
无
输出格式
无
输入输出样例
输入 #1
7 5 1 5 3 5 5 1 3 2 2 3 3 4 3
输出 #1
2 3 1 5
使用容器:std::multiset
mulitset是什么?
multiset是C++ STL里的一种容器。
头文件 #include<set>
multiset有什么性质?
- 里面的元素按顺序排列,默认升序。
- 不去重(这点和set是不同的)。
常用方法
multiset<int>q;
//定义一个multiset,尖括号里写类型
//如果是自定义类型,需要重载小于号
q.insert(x);
//插入一个数 x
q.clear();
//清空
q.erase(x);
//删除容器中的所有值为 x 的数
q.erase(it);
//删除容器中迭代器it指向的元素
q.empty();
//返回bool值,如果容器为空返回true,否则返回false
q.size()
//返回元素个数
q.begin();
//返回首个元素的迭代器
q.end();
//返回最后一个元素的下一个位置的迭代器
q.count(x);
//返回容器中 x 的个数
q.find(x);
//返回容器中第一个x的位置(迭代器),如果没有就返回q.end()
q.lower_bound(x);
//返回容器中第一个大于等于x的数的迭代器
q.upper_bound(x);
//返回容器中第一个大于x的数的迭代器
以上是常用的方法。
值得一提的是,lower_bound方法与upper_bound方法,都是内置二分查找,速度要比遍历暴力查找快很多。
分析题目
1. 查询 x 数的排名
排名,说白了就是排序之后的x的下标。
我们只要用lower_bound方法,找到第一个x的位置。
然后从begin开始往后遍历容器,只要达到这个位置,就输出当前下标即可。
2.查询排名为 x 的数
遍历容器,只要当前排名到达x,就输出当前值。
(因为multiset容器无法进行随机访问)
3.求 x 的前驱(前驱定义为小于 x,且最大的数)
前驱,也就是x的前一个。
我们只要用lower_bound方法找到第一个x的位置,然后输出上一个就OK了。
4.求 x 的后继(后继定义为大于 x,且最小的数)。
后继,也就是第一个大于x的数。
我们可以用upper_bound方法,直接找到这个值。
5.插入一个数 x
直接用insert方法插入即可。
坑点
题目描述其实是不全的,这一点我也是看讨论版才知道的。
-
如果x没有前驱要输出
-0x7fffffff
-
如果x没有后继要输出
0x7fffffff
我们可以提前把这两个值放到容器里,那么,没有前驱的数的前驱一定是-0x7fffffff
;没有后继的数的后继一定是0x7fffffff
。
代码
(详见注释)
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
multiset<int>q;
int n,t,x,order;
int main()
{
q.insert(-0x7fffffff);
q.insert(0x7fffffff);
//提前放入这两个数,避免错误
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&t,&x);
if(t==1)
{
auto it=q.lower_bound(x);
//auto是自动判断数据类型,只有C++14以上才支持
//可以写作multiset<int>::iterator,因为lower_bound方法返回的是迭代器
// it 取得 x 的位置
order=0;
//order为排名
for(auto i=q.begin();i!=it;i++,order++);
//这里的auto同理,也是迭代器
//这里就处理出了x的排名——order
printf("%d\n",order);
//输出order即为答案
}
else if(t==2)
{
order=-1;
//初值为-1是因为前面有一个-0x7fffffff,所以order要多跑一步
for(int i:q)
if(++order==x)
//缩写,order先自增一,再判断是否与x相等
//如果是(order++==x),那就是先判断再自增,这里要尤其注意
printf("%d\n",i);
//i就是容器里的值,输出i
//注意这里的for(:)循环,是只有C++11以上才支持的
//和auto一样,都是noip不能用的
//这种循环,i就是容器里的值而不是下标
//也可以使用迭代器循环,上面的循环等价于
/*
for(multiset<int>::iterator it=q.begin();it!=q.end();it++)
{
order++;
if(order==x)
printf("%d\n",*it);
}
*/
//这种循环是noip可以使用的
}
else if(t==3)
{
auto it=q.lower_bound(x);
//取得第一个大于等于x的值
//也就是第一个x的位置
//由于我们要取得前驱,所以it要自减一
printf("%d\n",*--it);
//这句是先自减,再输出,是缩写
//等价于:
/*
it--;
printf("%d\n",*it);
*/
//因为是迭代器(指针),所以输出前面加 *
}
else if(t==4)
{
printf("%d\n",*q.upper_bound(x));
//要取得后继,就是第一个大于x的值
//用upper_bound方法取得第一个大于x的迭代器
//输出即可
//因为是迭代器(指针),所以输出前面加 *
}
else
{
q.insert(x);
//直接添加即可
}
}
return 0;
}