c++的一些科技

pd_ds

需要

#include<bits/extc++.h>

using namespace __gnu_pbds;

using namespace __gnu_cxx;

__gnu_pbds::priority_queue

注意可能会与std::priority_queue冲突。

定义方法:__gnu_pbds ::priority_queue<T, Compare, Tag, Allocator>

  • T:类型名

  • Compare:严格弱化的比较类型

  • Tag:__gnu_pbds提供的五种堆,默认为pairing_heap_tag

    • pairing_heap_tag:配对堆(配对堆是一个支持插入,查询/删除最小值,合并,修改元素等操作的数据结构,是一种可并堆。有速度快和结构简单的优势,但由于其为基于势能分析的均摊复杂度,无法可持久化)。在实际测试中表现最好。

    • binary_heap_tag:二叉堆,表现并不是如官方文档说的那么好,在原生元素中表现可能要比配对堆好(用模板题排序测得,在luogu(c++14(GCC9))要比配对堆快几十毫秒)。

    • binomial_heap_tag:二项堆(二项堆(binomial heap)是一种类似于二叉堆的堆结构。与二叉堆相比,其优势是可以快速合并两个堆,因此它属于可合并堆(mergeable heap)抽象数据类型的一种。),合并操作时表现优于二叉堆,取堆顶元素的复杂度比二叉堆高。

    • rc_binomial_heap_tag:冗余计数二项堆,表现貌似比二项堆好点。

    • thin_heap_tag:除了合并是\(O(n)\)的,其它和斐波那契堆一样。

      综合来看,Tag中默认的pairing_heap_tag综合表现最好,竞赛中常用的也是这个。

  • Allocator:空间配置器,不造是啥,反正在竞赛中基本不会设计,想了解的大佬建议百度

成员函数:

  • push():向堆中压入一个元素并返回改元素位置的迭代器
  • pop():弹出堆顶
  • top():返回堆顶元素
  • size():返回元素个数
  • empty():返回是否为空
  • modify(iterator ,key)将迭代器位置的key变为传入的key,并排序
  • erase(iterator):删除迭代器处的键值
  • join(__gnu_pbds::priority_queue &other):将other合并到*this中并将other清空

\(\begin{array}{c|ccccc} {} & \text{push} & \text{pop} &\text{modify} &\text{erase} &\text{join}\\ \hline pairing\_heap\_tag& O(1) &均摊 \Theta(\log n)& 均摊\Theta(\log n)&均摊\Theta(\log n)&O(1) \\ binary\_heap\_tag&\Theta(\log n)&均摊\Theta(\log n)& \Theta(n)&\Theta(n)&\Theta(n)\\ binomial\_heap\_tag&均摊O(1)&\Theta(\log n)&\Theta(\log n)&\Theta(\log n)&\Theta(\log n)\\ rc\_binomial\_heap\_tag&O(1)&\Theta(\log n)&\Theta(\log n)&\Theta(\log n)&\Theta(\log n)\\ thin\_heap\_tag&O(1)&均摊\Theta(\log n)& 均摊O(n)&均摊\Theta(\log n)&\Theta(n) \end{array}\)

__gnu_pbds::tree

定义方法
__gnu_pbds::tree<Key,Mapped,Cmp_Fn=std::less<Key>,Tag=rb_tree_tag,Node_Update=null_tree_node_update,Allocator=std::allocator<char> >

  • Key:储存的元素类型,如果要储存相同元素,要用pair或结构体,并使用lower_boundupper_bound
  • Mapped:映射规则。若要该树为集合,此处是null_type,反之若要该树为map,此处填入value
  • Cmp_Fn:比较函子
  • Tag:选择底层数据结构,__gnu_pbds提供三种不同的平衡树
    • rb_tree_tag:红黑树,一般性能最好
    • splay_tree_tag:splay
    • ov_tree_tag:由vector实现
  • Node_Update:更新节点的方法,默认null_node_update,如果要使用order_of_key或者find_by_order,需要使用tree_order_statistics_node_update
  • Allocator:空间分配器类型

成员函数:

  • insert(x):插入元素,返回pair<iterator,bool>

  • erase(x):删除元素或迭代器,返回bool表示是否成功

  • order_of_key(x):返回x的排名

  • find_by_order(x):返回排名所对应的元素的迭代器

  • lower_bound(x):做lower_bound,返回迭代器

  • upper_bound(x):做upper_bound,返回迭代器

  • join(x):将x树并入当前树,删除x

  • split(x,b):将小于等于x的元素留在当前树,其余的留在b树

  • empty():返回是否为空

  • size:返回大小

    注意,join应保证键值不相交

模板:普通平衡树

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
#define pii pair<int,int>
#define mk make_pair
tree<pii,null_type,less<pii>,rb_tree_tag,tree_order_statistics_node_update> T;
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    int n;cin>>n;
    for(int i = 1;i <= n; ++i){
        int op,x;cin>>op>>x;
        switch(op){
            case 1:T.insert(mk(x,i));break;
            case 2:T.erase(T.lower_bound(mk(x,0)));break;
            case 3:cout<<T.order_of_key(mk(x,0))+1<<'\n';break;
            case 4:cout<<(*T.find_by_order(x-1)).first<<'\n';break;
            case 5:cout<<(*--T.lower_bound(mk(x,0))).first<<'\n';break;
            case 6:cout<<(*T.upper_bound(mk(x,n))).first<<'\n';break;
        }
    }
}

rope

需要using namespace __gnu_cxx;

可以实现块状链表,本质是可持久化平衡树,不支持任何与数值有关的操作(如查询排名),不适用于单个字符的修改

\(crope = rope<char>\),可以将其理解为重型string

  • 如何可持久化?

rope<int> * f[1000];
f[i] = new rope<int>(*f[i-1]);

时间复杂度\(O(1)\)

  • 基本操作

    1. insert(pos,x) : 在pos添加x,支持插入数组
    2. erase(pos,x) : 在pos后删除x个元素
    3. push_back(x) : 在末尾添加元素
    4. substr(pos,x) : 从pos开始提取x
    5. copy(pos,len,x) : [pos,len]用x替代

常数有点大,空间复杂度玄学。

可持久化数组板子。
这篇代码只能拿80

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll=long long;using ull=unsigned long long;
const int N = 1e6 + 10;
vector<rope<int> > rp(1);
int n,m,a[N];
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>m;
    for(int i = 1,x;i <= n; ++i) cin>>a[i];
    rp[0].append(a,n+1);
    for(int i = 1;i <= m; ++i){
        int ver,op,loc,val;
        cin>>ver>>op>>loc;
        rp.emplace_back(rp[ver]);
        if(op == 1){
            cin>>val;
            rp.back().replace(loc,val);
        }
        else{
            cout<<rp.back().at(loc)<<'\n';
        }
    }
}

文艺平衡树

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
using namespace __gnu_cxx;
using namespace __gnu_pbds;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
using ll = long long;using ull = unsigned long long;
char *p1,*p2,buf[1<<23];
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
#ifdef linux
#define pc putchar_unlocked
#else
#define pc putchar
#endif
namespace IO{
    template<typename T>inline bool read(T &x){x=0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x<<1)+(x<<3)+(s^48);if(!f) x=~x+1;return true;}
    inline bool read(double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
    inline bool read(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false; for(;s!=' '&&s!='\n'&&s!=EOF;s=gc())str.push_back(s);return true;}
    inline bool read_line(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str.push_back(s);}return true;}
    inline bool read_line(char *str){int len=0;char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str[len]=s;len++;}str[len]='\0';return true;}
    inline bool read(char &s){char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF||x==' '||x=='\n')return false;s=x;return true;}
    inline bool read(char *s){int len=0;char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF)return false;for(;x!=' '&&x!='\n'&&x!=EOF;x=gc())s[len++]=x;s[len]='\0';return true;}
    template<class T,class... Args> inline bool read(T &x,Args&... args){return (read(x)&&read(args...));}
    template<class T>inline void write(T x){static T st[45];int top=0;if(x<0)x=~x+1,pc('-');do{st[top++]=x%10;}while(x/=10);while(top)pc(st[--top]^48);}
    inline void write(char x){pc(x);}
    inline void write(string s){for(int i=0;s[i];++i) pc(s[i]);}
    inline void write(char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
    inline void write(const char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
    template<class T,class... Args> inline void write(T x,Args... args){write(x);write(args...);}
}using namespace IO;
rope<int> a,b;
signed main(){
    #ifndef ONLINE_JUDGE
        infile("in.in");outfile("out.out");
    #endif
    int n,m;
    read(n,m);
    for(int i = 0;i < n; ++i){
        a.push_back(i+1);
        b.push_back(n-i);
    }
    while(m--){
        int l,r;read(l,r);
        l--,r--;
        rope<int> res = a.substr(l,r-l+1);
        a = a.substr(0,l)+b.substr(n-r-1,r-l+1)+a.substr(r+1,n-r-1);
        b = b.substr(0,n-r-1)+res+b.substr(n-l,l);
    }
    for(int i = 0;i < n; ++i) write(a[i],' ');
}

对于数值操作,可以二分插入。
模板是可持久化平衡树。

hash

有两个,分别是使用拉链法的cc_hash_table,使用探测法的gp_hash_table。有的博客说探测法容易被卡,但是实测还是探测法跑的更快,所以推荐使用探测法。

定义方法 :

gp/cc_hash_table<typename Key, typename Mapped>

其实后面还有,但是没看懂,OI中一般也不用,所以就省略了。

  • key : 是键值。
  • Mapped : 是实值,如果不想用,那么就在此处填null_type

成员函数 :

  • [] : 和map一样。若没有也是加入新的节点。
  • find : 查找,和map一样。若没有返回end()
posted @ 2024-07-21 20:30  CuFeO4  阅读(26)  评论(4编辑  收藏  举报