[关闭]
@exut 2024-11-04T02:48:02.000000Z 字数 4839 阅读 221

浅谈pbds

科技


CCF在 年出版的新规定中已经允许使用下划线库函数了,pbds 的合法性已经在noi系列赛事得到了承认

是这样的大家平时写代码是不是经常会用到 stl

但是这个玩意是不是一边一边嫌弃:set 明明是平衡树但是功能严重阉割,deque 空间时间都有毛病,priority_queue 不是可并堆...

烦死了,蠢B STL!!!!

有没有什么好的替代方案呢,假如说考场上不想写平衡树不想写可并堆(deque 很好手写那就手写得了)

现在介绍——pbds库

头文件与namespace

  1. #include <bits/extc++.h>
  2. using namespace __gnu_cxx;
  3. using namespace __gnu_pbds;

背下来就可以了

priority_queue

是的,pbds 也有优先队列,所以为什么不把这玩意代替掉默认的那个呢

注意事项

参数

定义的时候不需要加入 vector<T>

一共需要三个参数:

其中仿函数可以用 greater<T>,less<T>,也可以手写一个自己的

例如:

  1. struct myCmp{//按照b降序(小根)
  2. bool operator()(node x, node y){//此时要将y想象成 排在优先队列前面的元素
  3. return x.b < y.b;
  4. }
  5. };

需要注意的是堆的类型,其实有很多种

值得吐槽的是,配对堆也是唯一一个复杂度没能完全证明的

函数

你得知道 stl 里面那个萎了的堆有的函数他都是有的

唯一需要注意的是一个有趣的地方,这里的 push有返回值的,会返回一个迭代器

modify修改

依赖于迭代器才能实现的修改

注意的是 pbds 的迭代器叫做 point_iterator

我们可以用一个迭代器数组存下每个元素对应的位置,这样就可以快速修改元素了~

迭代器的声明方式:

  1. __gnu_pbds::priority_queue::point_iterator it

修改的方式是:

  1. q.modify(it,val)

含义是把迭代器 it 位置的元素改为

迭代器数组的初始化是 NULL,我们也可以借助一个元素对应的迭代器是不是 NULL 来判断一个元素在不在队列里

来,我们利用这个玩意来写一个dijskra

  1. #include<bits/stdc++.h>
  2. #include<bits/extc++.h>
  3. #define int long long
  4. using namespace __gnu_cxx;
  5. using namespace __gnu_pbds;
  6. using namespace std;
  7. const int N=2e5+5;
  8. const int inf=1e12;
  9. typedef pair<int,int> pii;
  10. vector<pii> e[N];
  11. __gnu_pbds::priority_queue<pii,greater<pii>,pairing_heap_tag> q;
  12. __gnu_pbds::priority_queue<pii,greater<pii>,pairing_heap_tag>::point_iterator its[N];
  13. int dis[N];
  14. int n,m,s;
  15. void dijskra(int s){
  16. q.clear();
  17. for(int i=1;i<=n;i++) dis[i]=inf;
  18. its[s]=q.push({0,s});
  19. dis[s]=0;
  20. #define v g.first
  21. #define w g.second
  22. while(!q.empty()){
  23. int u=q.top().second;
  24. q.pop();
  25. for(auto g:e[u]){
  26. if(dis[v]>dis[u]+w){
  27. dis[v]=dis[u]+w;
  28. if(its[v]==NULL){
  29. its[v]=q.push({dis[v],v});
  30. }else{
  31. q.modify(its[v],{dis[v],v});
  32. }
  33. }
  34. }
  35. }
  36. #undef v
  37. #undef w
  38. }
  39. signed main(){
  40. ios::sync_with_stdio(0);
  41. cin.tie(0),cout.tie(0);
  42. cin>>n>>m>>s;
  43. for(int i=1;i<=m;i++){
  44. int u,v,w;
  45. cin>>u>>v>>w;
  46. e[u].push_back(make_pair(v,w));
  47. }
  48. dijskra(s);
  49. for(int i=1;i<=n;i++) cout<<dis[i]<<" ";
  50. }

这样的理论复杂度是比正常的其实要好的,但是由于常数原因咳咳

但是确实很好玩

join合并

是的~这是可并堆哦

使用方法:

  1. a.join(b);

会把 中元素全部扔到 中并自动清空

我们借助这个玩意可以写左偏树的板子:

  1. #include<bits/stdc++.h>
  2. #include<bits/extc++.h>
  3. using namespace std;
  4. using namespace __gnu_cxx;
  5. using namespace __gnu_pbds;
  6. const int N=1e5+5;
  7. typedef pair<int,int> pii;
  8. __gnu_pbds::priority_queue<pii,greater<pii>,pairing_heap_tag> q[N];
  9. int n,m;
  10. int fa[N];
  11. bool del[N];
  12. int find(int x){
  13. return (x==fa[x]?x:(fa[x]=find(fa[x])));
  14. }
  15. int main(){
  16. cin>>n>>m;
  17. for(int i=1;i<=n;i++){
  18. int x;
  19. cin>>x;
  20. q[i].push(make_pair(x,i));
  21. fa[i]=i;
  22. }
  23. while(m--){
  24. int opt,x,y;
  25. cin>>opt>>x;
  26. if(opt==1){
  27. cin>>y;
  28. int fx=find(x),fy=find(y);
  29. if(fx==fy or del[x] or del[y]) continue;
  30. q[fx].join(q[fy]);
  31. fa[fy]=fx;
  32. }
  33. else{
  34. if(del[x]) {cout<<"-1\n";continue;}
  35. int fx=find(x);
  36. cout<<q[fx].top().first<<"\n";
  37. del[q[fx].top().second]=1;
  38. q[fx].pop();
  39. }
  40. }
  41. }

慢是慢你就说是不是不需要脑子吧

hash_table

pbds 提供了一个非常好用的哈希表,简单来说就反正比 unordered_map 好的多,没那么容易被卡,速度也快很多

一定要注意的是,hash_table 对标的是 unordered_map 不是 map,他的复杂度是线性的,而且不支持排序,不要拿他和 map 比较了,没有意义功能都不一样

概述

两种哈希表:
- cc_hash_table<T,T>这是拉链法
- gp_hash_table<T,T> 这个是探测法

经测试,cc 的读入输出会比 gp 略微快一些,但是 gp 仅仅是牺牲了大约百分之十的读写速度就换来了快 倍的插入删除和清除速度,是更为划算的,大多数情况都建议选用 gp,但是必须注意的是不是任意时候

一个例子是OIFC20241023,本题中我的代码在 unordered_map 是有概率能过(看评测机状态,众所周知xyd的评测机波动巨大),使用 gp 只有 ,然后 cc 稳定可以过

原因是此时的 hash 的主要作用在于映射,也就是读写部分,此时 gp 在读写的劣势会被放大,也就是说当不怎么需要多次插入删除而主要需要读取数值的时候, cc 会比 gp 更为优秀

在给出的例子中的原因主要是插入次数顶多是 级别的,但是查询是 级别的,当然这还是不能解释为什么比 unordered_map 还要慢

rope

一个奇怪的东西

值得注意的是rope的复杂度有点鬼,有人认为底层是块状链表,那复杂度就是根号,有人说是可持久化平衡树,那复杂度就是对数,本文暂时写作根号,假如有一天确定了是对数那就自动把本文所有根号看成对数吧

rope 的声明

  1. rope<T> s

跟什么栈啊队列啊是一样的,支持结构体,空间动态

强制转化类型与赋值

  1. int f[114514],n;
  2. int main(){
  3. cin>>n;
  4. for(int i=0;i<n;i++) cin>>f[i];
  5. rope<int> fa(f);
  6. for(int v:fa) cout<<v<<endl;
  7. }

但是注意被用于转化的普通数组的下标 处必须非 ,比较逆天

字符串rope(crope)

  1. crope c;
  2. rope<char >c;

这两种写法是等价的

函数如下:

  1. crope c;
  2. c.push_back(x);//在c末尾添加x
  3. c.insert(p,x);//在c下标为p的位置插入一个x
  4. c.insert(p,s,n);//将字符串 s 的前n位插在p处
  5. c.erase(p,x);//从下标p处开始删除x个元素
  6. c.replace(p,s);//从下标p开始把字符串s覆盖上去
  7. c.copy(p,n,s);//从下标p开始的n个字符换成字符串s
  8. c.substr(p,x);//从p开始截取x个元素
  9. c[x]/c.at(x);//访问元素
  10. c.append(s,p,n);
  11. //把字符串 s 中从下标 p 开始的 n 个字符连接到 a 的结尾
  12. //如没有参数 n 则把字符串 s 中下标 p 后的所有字符连接到 a 的结尾
  13. //如参数 p 也没有则把整个字符串 s 连接到 a的结尾

除了 push_back这种单个元素操作以外复杂度都是根号

数组rope

其实同理

数组rope 额外可以用 append(x) 来尾端插入一个数字

当然常见的size什么的函数当然是都有的

运用——可持久化操作

例题

怎么实现

是的没错暴力存每个版本就可以了

你说空间?这题能过,考场上骗分多好用啊

值得注意的是rope互相直接赋值的复杂度也是神奇的根号

  1. #include<bits/stdc++.h>
  2. #include<bits/extc++.h>
  3. using namespace std;
  4. using namespace __gnu_pbds;
  5. using namespace __gnu_cxx;
  6. typedef double db;
  7. typedef pair<int,int> pii;
  8. #define mpi make_pair
  9. const int N=2e5+5;
  10. const int mod=998244353;
  11. crope now,pst[N];
  12. int cnt,n;
  13. int main(){
  14. cin>>n;
  15. while(n--){
  16. char opt;
  17. cin>>opt;
  18. if(opt=='T'){
  19. char x;
  20. cin>>x;
  21. now.push_back(x);
  22. pst[++cnt]=now;
  23. }else if(opt=='U'){
  24. int q;
  25. cin>>q;
  26. now=pst[cnt-q];
  27. pst[++cnt]=now;
  28. }else{
  29. int q;
  30. cin>>q;
  31. cout<<now[q-1]<<"\n";
  32. }
  33. }
  34. }

tree平衡树

pbds 的平衡树必须注意的是速度问题,常数较为巨大,相比手写根据网上数据大概会比相同的手写慢个六分之一,这是封装必然的代价

附录

个人常用头文件与 define 等:

  1. #include<bits/stdc++.h>
  2. #include<bits/extc++.h>
  3. using namespace std;
  4. using namespace __gnu_pbds;
  5. using namespace __gnu_cxx;
  6. typedef double db;
  7. typedef pair<int,int> pii;
  8. #define mp make_pair
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注