科技改变生活
前言
本来一直被畏于巨长的声明,没有学这个东西…
直到 棘手的操作 这道题,pb_ds
模拟实现的两个log的做法不仅好写的一批,连时间竟然也把我单log的左偏树爆踩了???
…
我选择打不过就加入…
注意:本文一切服从于OI的实用性,可能会舍弃一些在OI领域对于我个人用处不大的功能。
哈希表的功能我选择舍弃了,因为哈希表本身并不是难于编写的结构。
auto
非常方便。
(可能会动态更新?)
头文件
pb_ds一族的万能头:
#include<bits/extc++.h>
就是把普通万能头的 std
改成了 ext
(extend)。
堆
总结
pbds的堆与stl相比,额外支持的函数如下:
join(__gnu_pbds :: priority_queue &other)
: 把 other 合并到 *this 并把 other 清空。erase(point_iterator)
: 把迭代器位置的键值从堆中擦除。modify(point_iterator, const key)
: 把迭代器位置的 key 修改为传入的 key,并对底层储存结构进行排序。
宏定义&声明
(这里的宏定义只是笔者个人的习惯,下文会默认使用。)
namespace pd = __gnu_pbds;
#define It pd::priority_queue<int>::point_iterator
pd::priority_queue<int,cmp>q;
其中 point_operator
是 pb_ds
族所使用的迭代器。
cmp
是自定义的小于号,默认为 less<int>
,也可以改为 greater<int>
,或者使用和std类似的重载方式:
struct cmp{bool operator ()(const int &x,const int &y){return x<y;}
};
pd::priority_queue<int,cmp>q;
实际上比较符之后还有两栏可以自定义,但在OI应用中几乎都是默认即可(默认的堆实现方式是配对堆)。
成员函数
(完全复制 OI-wiki )
push()
: 向堆中压入一个元素,返回该元素位置的迭代器。pop()
: 将堆顶元素弹出。top()
: 返回堆顶元素。size()
返回元素个数。empty()
返回是否非空。modify(point_iterator, const key)
: 把迭代器位置的 key 修改为传入的 key,并对底层储存结构进行排序,返回该元素位置的迭代器。erase(point_iterator)
: 把迭代器位置的键值从堆中擦除,返回值为void
。join(__gnu_pbds :: priority_queue &other)
: 把 other 合并到 *this 并把 other 清空。
复杂度
push
和 join
是 O(1)O(1)O(1),其余均为最差 O(n)O(n)O(n),均摊 O(logn)O(\log n)O(logn)。
迭代器失效
点失效保证
与stl不同,pbds的迭代器非常强大。
只要迭代器没被删除,就始终保持有效。
即使执行过 modify
操作都是有效的!
未定义行为
点迭代器被删除,访问时依然不会RE,而是会返回0。
但是如果开启 -fsanitize=address
,就会出现报错 heap-use-after-free
。
点迭代器被删除,再将其作为某个函数的参数传入时(如 modify
erase
),会直接RE。
平衡树
总结
优势:查询排名和k大。
似乎还有一些更加神奇的操作?但是经过 维持记忆开销和获得效果之间的性价比 权衡后,我并不想get了…
注意:pbds的tree必须是不可重集合!否则不仅会吞掉重复元素,在合并两棵树的时候还会发生不可预知的错误。具体实现上,可以使用
double
或者pair
来解决该问题(个人更倾向于后者)。
声明
pd::tree<key,mapped,cmp ,pd::rb_tree_tag,pd::tree_order_statistics_node_update>q;
key
:排序所用的键值类型。
mapped
:映射类型。如果写成 null_type
则是无映射类型,相当于 std::set
,否则就有些类似 std::map
。
pd::rb_tree_tag
:底层使用红黑树的实现,看OIwiki和于纪平的课件各项指标都不错,就背这一个吧。
pd::tree_order_statistics_node_update
:统计更新树的节点排名,没有它就没法支持查排名和k大!所以必须用…(首字母记忆:脱欧是内忧)
成员函数
insert(key)
:向树中插入一个元素 x(若为map则是pair),返回std::pair<point_iterator, bool>
。erase(key/iterator)
:从树中删除一个元素/迭代器 x,返回一个 bool 表明是否删除成功。order_of_key(key)
:返回 x 以 cmp 比较的排名(unsigned long
)。(排名)find_by_order(int)
:返回 cmp 比较的排名所对应元素的迭代器。(k大)lower_bound(key)
:以 cmp 比较做 lower_bound,返回迭代器。upper_bound(key)
:以 cmp 比较做 upper_bound,返回迭代器。join(tree)
:将 x 树并入当前树,前提是两棵树的类型一样,x 树被删除。split(x(key),b(tree))
:先清空 b 树,再以 cmp 比较,小于等于 x 的属于当前树,其余的属于 b 树。empty()
:返回是否为空。size()
:返回大小。
这里的“排名”是指大于该元素的元素个数。