浅谈平板电视

因为没有介绍平板电视而被打回题解,于是有了这篇博客介绍平板电视

前言:本博客只谈一些关于 oi 中实用的平板电视技巧,如果想要更详细具体的了解平板电视,推荐两篇博客(在最底下),写的很详细,本博客很大程度上是根据这两位大佬的博客总结形成的。

简介

平板电视,即 __gnu_pbds,全称 “Policy based data structures”,是 C++ 的一个扩展库。里面包装了许多的数据结构,大致有:哈希表、堆、平衡树、字典树等,堪称“比 STL 还 STL”。

使用平板电视一般需要加上一下头文件:

#include<ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;

虽然网上众博客介绍调用不同的数据结构还有不同的头文件需要添加,但是本人亲测只需要加上面的头文件就足够了。本来觉得很奇怪,于是看了一下头文件内部,发现这个头文件中包含了 hash,tree,trie 三个数据结构需要的头文件(用堆的话貌似需要再加头文件)。
其实平板电视也有万能头:

#include<bits/extc++.h>

但是我用 vscode 没有办法编译,会显示一个无法打开的内置文件,貌似 dev c++ 也会有相同的问题,因此在这里不推荐用这种头文件。

还有一个命名空间:

using namespace __gnu_pbds;

长得和 __gnu_cxx 很像嘛,其实一般用到 __gnu_cxx 这个命名空间是在用 rope 的时候,rope 也是一个很强大的库,rope 本身内部是块状链表实现的,可以用于一些需要可持久化平衡树的题目,我们下次再讲。

哈希表

头文件

#include<ext/pb_ds/hash_policy.hpp>//不用再加

概述

平板电视中有两种类型的哈希表:

  • cc_hash_table< 类型名 , 类型名 >mp拉链法
  • gp_hash_table< 类型名 , 类型名 >mp探测法

网上众多博主实测都说探测法更快(虽然我没测过)。

map 均摊是 \(O(\log n)\) 的,而 hash_table 均摊是 \(O(1)\),但是与 STL 中的 unordered_map 相比,hash_table 更慢。

平衡树

头文件

#include<ext/pb_ds/tree_policy.hpp>//不用再加

概述

平板电视中平衡树有很多,但是一般比较常用的只有两种:

  • splay_tree_tag
  • rb_tree_tag

需要注意的是平板电视中的平衡树常数比较大,而两种平衡树中红黑树的快一些,因此一般也只使用 rb_tree_tag。

命名方式:

tree<类型,null_type,less<类型>,rb_tree_tag,tree_order_statistics_node_update>rbt;

平衡树中的库函数有以下这些:

  • insert(x)//与 set 相同,插入 x
  • erase(x)//与 set 相同,删除 x
  • find_by_order(k)//查询平衡树中第 k 名的值,返回迭代器
  • order_of_key(x)//求 x 的排名,返回整数,注意最后要+1
  • lower_bound(x)//与 set 相同,求大于等于 x 的最小值,返回迭代器
  • upper_bound(x)//与 set 相同,求大于 x 的最小值,返回迭代器
  • join(b)//将 b 并入一颗平衡树,前提是两棵树类型相同并且没有重复元素
  • split(v,b)//分裂平衡树,key 小于等于 v 的元素属于当前平衡树,其余属于 b
  • ......

除此之外,还可以自定义平衡树的功能,但是我没有用过,因此就不在这里介绍了。

实战 P3369普通平衡树

#include<iostream>
#include<ext/pb_ds/assoc_container.hpp>
using namespace std;
typedef long long ll;
typedef pair<ll,ll>PLL;
#define x first
#define y second
const ll INF=0x3f3f3f3f3f3f3f3f;
using namespace __gnu_pbds;
tree<PLL,null_type,std::less<PLL>,rb_tree_tag,tree_order_statistics_node_update>rbt;
int main() 
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    ll n,opt,k;
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>opt>>k;
        switch(opt) 
        {
            case 1:
                rbt.insert({k,i});
                break;
            case 2:
                rbt.erase(rbt.lower_bound({k,0}));
                break;
            case 3:
                cout<<rbt.order_of_key({k,0})+1<<"\n";
                break;
            case 4:
                cout<<(*rbt.find_by_order(k-1)).x<<"\n";
                break;
            case 5:
                cout<<(*(--rbt.lower_bound({k,0}))).x<<"\n";
                break;
            case 6:
                cout<<(*rbt.upper_bound({k,INF})).x<<"\n";
                break;
        }
    }
    return 0;
}

一点小技巧:

一般平板电视的迭代器写起来都比较长,幸好 c++11 中引入了 auto,大大方便了我们存储迭代器。

头文件

字典树

还不会,以后学。

本人亲测:如果用平板电视的堆,貌似即使写了 using namespace __gnu_pbds命名空间,但是还是需要手动添加__gnu_pbds::,不然会和 STL 中的堆冲突,过不了编。

不过 STL 中的 priority_queue 用起来已经很简单了吧,何必再用平板电视的堆,还要写很长的头文件。

实际上是我还不会,先挖个坑,以后再补。

不过貌似平板电视的堆还能实现左偏树的功能,虽然左偏树也不是特别难写

参考资料:

Gh0st_Lx大佬的博客

繁凡さん大佬的博客

本文立的 flag:

  • 介绍 rope
posted @ 2023-07-23 21:41  week_end  阅读(72)  评论(0编辑  收藏  举报