模板:pb_ds指南

科技改变生活

前言

本来一直被畏于巨长的声明,没有学这个东西…
直到 棘手的操作 这道题,pb_ds模拟实现的两个log的做法不仅好写的一批,连时间竟然也把我单log的左偏树爆踩了???

我选择打不过就加入…

注意:本文一切服从于OI的实用性,可能会舍弃一些在OI领域对于我个人用处不大的功能。

哈希表的功能我选择舍弃了,因为哈希表本身并不是难于编写的结构。

auto 非常方便。

(可能会动态更新?)

头文件

pb_ds一族的万能头:

#include<bits/extc++.h>

就是把普通万能头的 std 改成了 ext (extend)。

总结

pbds的堆与stl相比,额外支持的函数如下:

  1. join(__gnu_pbds :: priority_queue &other): 把 other 合并到 *this 并把 other 清空。
  2. erase(point_iterator): 把迭代器位置的键值从堆中擦除。
  3. 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_operatorpb_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

  1. push(): 向堆中压入一个元素,返回该元素位置的迭代器。
  2. pop(): 将堆顶元素弹出。
  3. top(): 返回堆顶元素。
  4. size() 返回元素个数。
  5. empty() 返回是否非空。
  6. modify(point_iterator, const key): 把迭代器位置的 key 修改为传入的 key,并对底层储存结构进行排序,返回该元素位置的迭代器。
  7. erase(point_iterator): 把迭代器位置的键值从堆中擦除,返回值为 void
  8. join(__gnu_pbds :: priority_queue &other): 把 other 合并到 *this 并把 other 清空

复杂度

pushjoin O ( 1 ) O(1) O(1),其余均为最差 O ( n ) O(n) O(n)均摊 O ( log ⁡ n ) 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大!所以必须用…(首字母记忆:脱欧是内忧

成员函数

  1. insert(key):向树中插入一个元素 x(若为map则是pair),返回 std::pair<point_iterator, bool>
  2. erase(key/iterator):从树中删除一个元素/迭代器 x,返回一个 bool 表明是否删除成功。
  3. order_of_key(key):返回 x 以 cmp 比较的排名(unsigned long)。(排名)
  4. find_by_order(int):返回 cmp 比较的排名所对应元素的迭代器。(k
  5. lower_bound(key):以 cmp 比较做 lower_bound,返回迭代器。
  6. upper_bound(key):以 cmp 比较做 upper_bound,返回迭代器。
  7. join(tree):将 x 树并入当前树,前提是两棵树的类型一样,x 树被删除
  8. split(x(key),b(tree)):先清空 b 树,再以 cmp 比较,小于等于 x 的属于当前树,其余的属于 b 树。
  9. empty():返回是否为空。
  10. size():返回大小。

这里的“排名”是指大于该元素的元素个数。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值