平板电视(pb_ds)应用

原文地址

定义

包含:ext/pb_ds/priority_queue.hpp
声明:__gnu_pbds::priority_queue
模板参数:

    template < typename Value_Type ,
    typename Cmp_Fn = std :: less < Value_Type >,
    typename Tag = pairing_heap_tag ,
    typename Allocator = std :: allocator <char > >
    class priority_queue

Value_Type:类型
Cmp_Fn:自定义比较器
Tag:堆的类型。可以是binary_heap_tag(二叉堆)binomial_heap_tag(二项堆)rc_binomial_heap_tagpairing_heap_tag(配对堆)thin_heap_tag
Allocator:不用管

使用

相比STL中的priority_queue,可以
• 用begin()和end()获取迭代器从而遍历
• 删除单个元素 void erase(point_iterator)
• 增加或减少某一元素的值 void modify(point_iterator, const_reference)
• 合并 void join(priority_queue &other),把other合并到*this,并把other清空

性能分析

五种操作:push、pop、modify、erase、join
• pairing_heap_tag:push和joinO(1),其余均摊O(logn)
• binary_heap_tag:只支持push和pop,均为均摊O(logn)
• binomial_heap_tag:push为均摊O(1),其余为O(logn)
• rc_binomial_heap_tag:push为O(1),其余为O(logn)
• thin_heap_tag:push为O(1),不支持join,其余为O(logn);但是如果只有increase_key,modify均摊O(1)
• 不支持不是不能用,而是用起来很慢
经过实践检测得到的结论:
• Dijkstra算法中应用pairing_heap_tag,速度与手写数据结构相当。
• 配对堆在绝大多数情况下优于二项堆
• 只有push,pop和join操作时,二叉堆速度较快
• 有modify操作时,可以考虑thin_heap_tag或者配对堆,或手写数据结构。

定义

包含:ext/pb_ds/assoc_container.hpp和ext/pb_ds/tree_policy.hpp
声明:__gnu_pbds::tree

    typename Key , typename Mapped ,
    typename Cmp_Fn = std :: less <Key >,
    typename Tag = rb_tree_tag ,
    template <
        typename Const_Node_Iterator ,
        typename Node_Iterator ,
        typename Cmp_Fn_ , typename Allocator_ >
    class Node_Update = null_tree_node_update ,
    typename Allocator = std :: allocator <char > >
class tree ;

Tag

tree的类型,可以是rb_tree_tag,splay_tree_tag,ov_tree_tag
Node_Update:可以为空,也可以用pb_ds自带的tree_order_statistics_node_update,这样这个tree就会获得两个函数find_by_order和order_of_key
• iterator find_by_order(size_type order) 找第order+1小的元素的迭代器,如果order太大会返回end()
• size_type order_of_key(const_key_reference r_key) :询问这个tree中有多少比r_key小的元素

使用

与map使用方法基本相同,包括

begin(),end(),size(),empty(),clear(),find(const Key),lower_bound(const Key),upper_bound(const Key),erase(iterator),erase(const Key),insert(const pair<>  ),operator[]

如果想改成set,只需要将第二个参数Mapped改为null_type(在4.4.0及以下版本的编译器中应用null_mapped_type)就可以了。此时迭代器指向的类型会从pair变成Key,和set几乎没有区别。
当然还有一些其他用法,如:
• void join(tree &other) 把other中所有元素移动到*this上(值域不能相交,否则抛出异常。
• void split(const_key_reference r_key, tree &other) 清空other,然后把*this中所有大于r_key的元素移动到other。

自定义Node_Update使用方法。

以下代码实现了区间求和

template < class Node_CItr , class Node_Itr ,
    class Cmp_Fn , class _Alloc  >
struct my_node_update {
    virtual Node_CItr node_begin () const = 0;
    virtual Node_CItr node_end () const = 0;
    typedef int metadata_type ;
    inline void operator ()( Node_Itr it , Node_CItr end_it ){
        Node_Itr l = it. get_l_child (), r = it. get_r_child ();
        int left = 0, right = 0;
        if(l != end_it ) left = l. get_metadata ();
        if(r != end_it ) right = r. get_metadata ();
        const_cast < metadata_type &>( it. get_metadata ())
            = left + right + (* it)-> second ;
    }
    inline int prefix_sum (int x) {
        int ans = 0;
        Node_CItr it = node_begin ();
        while (it != node_end ()) {
            Node_CItr l = it. get_l_child (), r = it. get_r_child ();
            if( Cmp_Fn ()(x, (* it)-> first )) it = l;
            else {
                ans += (* it)-> second ;
                if(l != node_end ()) ans += l. get_metadata ();
                it = r;
            }
        }
        return ans;
    }
    inline int interval_sum (int l, int r){
        return prefix_sum (r) - prefix_sum (l - 1);
    }
}
int main() {
    tree <int , int , std :: less <int >, rb_tree_tag , my_node_update > T;
    T [2] = 100; T [3] = 1000; T [4] = 10000;
    printf ("%d\n", T. interval_sum (3, 4));
    printf ("%d\n", T. prefix_sum (3));
}

注意:

• 对Node_Itr可以做的事情:用get_l_child和get_r_child获取左右儿子,用两个星号(一个星号只是获取了iterator)获取节点信息,用get_metadata获取节点额外信息。
• operator()的功能是将节点it的信息更新,传入的end_it表示空节点。

性能分析

和手写数据结构差不多,rb_tree_tag要更快。

哈希

定义

包含:ext/pb_ds/assoc_container.hpp和ext/pb_ds/hash_policy.hpp
声明:

__gnu_pbds::cc_hash_table <Key, Mapped>
__gnu_pbds::gp_hash_table <Key, Mapped>

使用

支持operator[]和find

总结

• priority_queue,与STL相比支持了modify,erase和join
• tree,相当于STL中的set/map,还支持split和join,运用tree_order_statistics_node_update还支持查询rank和k小值;更可以自定义Node_Update来维护更多信息。
• (目前比赛环境中)STL没有的两种hash_table
• 无脑用pb_ds代替std::set/map/priority_queue不会使程序变得更慢

参考文献

于纪平WC2015《C++的pb_ds库在OI中的应用》
感谢saffah让我们认识到平板电视的神奇之处!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值