前言
虽然很久以前(6月)在我们刚学并查集的时候 QYC 就给我们讲了左偏树可以拿来做这道题,但是左偏树作为拓展内容还是稍有难度,最近在 gcc 中看到 pb_ds 库,发现非常好用,于是就有了这种偷懒解法。
pb_ds 库
pb_ds 库是内置于 GCC 中的一种拓展库,可以在 CCF 系列比赛中使用。
pb_ds 库中提供了许多好用的数据结构,比如远快于 unordered_map
(umap 常数太大)的 gp_hash_table
(查探法)和 cc_hash_table
,还有我们接下来要用到的各种不同于 STL 的配对堆、二顶堆等其它在某些方面优于二叉堆的结构,以及在 C2025 现阶段尚未学习的各种平衡树。
下面重点讲一下 pb_ds 平板电视 库中的 __gnu_pbds::priority_queue
。
引入
可以使用:
1
| #include <ext/pb_ds/priority_queue.hpp>
|
来引入 pb_ds 的 pq,但是你也可以通过以下方式图方便:
1 2 3 4
| #include <bits/extc++.h> using namespace std; using namespace __gnu_cxx; using namespace __gnu_pbds;
|
一次性导入包括 std::
在内的所有 GCC 内置库。
上面的 bits/extc++.h
在 TDM-GCC 下会报错,需要手动找到文件(编译器会提示)删除这一段:
1 2 3 4
| #ifdef _GLIBCXX_HAVE_ICONV #include <ext/codecvt_specializations.h> #include <ext/enc_filebuf.h> #endif
|
使用
使用以下方式来定义一个大根堆(比起STL少一个定义堆存储类型的参数,多一个类型声明):
1 2 3 4 5
| __gnu_pbds::priority_queue<type,less<type>,pairing_heap_tag>
__gnu_pbds::priority_queue<type>
|
大体上和 STL 差不多,只不过支持了 Remove()
、Modify()
和 Merge()
操作:
Merge 复杂度 O(1)
Remove 和 Modify 需要迭代器支持:
1 2 3
| auto it=pq.push(114514); pq.modify(it,1919810); pq.remove(it);
|
其它部分可以参见:https://oi-wiki.org/lang/pb-ds/pq/
解析
知道了可并堆的用法,我们就可以做这道题了。
我采用了这样的方案:维护一个数组 pq a[]
存储每一个猴子所在集合的朋友,再维护一个并查集来映射每一个猴子所在的集合,每次争斗后取出堆顶各自减半,再将两猴子的堆合并到第一个猴子那边,并合并并查集即可。
这样是不是比左偏树什么的简单多了qwq
代码
下面是这道题的代码,其实也就很简单了
注释版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <bits/extc++.h> using namespace std; using namespace __gnu_pbds; using namespace __gnu_cxx; __gnu_pbds::priority_queue<int> pq[100005]; int n,m,fa[100005]; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } inline void merge(int x,int y){ int u=find(x),v=find(y); if(x!=y) fa[v]=u; } int main(){ ios::sync_with_stdio(0); cin>>n,iota(fa,fa+1+n,0); for(int i=1,t;i<=n;i++) cin>>t,pq[i].push(t); cin>>m; for(int i=1,x,y,t;i<=m;i++){ cin>>x>>y; t=pq[find(x)].top(),pq[find(x)].pop(),pq[find(x)].push(t/2); t=pq[find(y)].top(),pq[find(y)].pop(),pq[find(y)].push(t/2); pq[find(x)].join(pq[find(y)]),merge(x,y); cout<<pq[find(x)].top()<<endl; } return 0; }
|
极速版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <bits/extc++.h> using namespace std; using namespace __gnu_pbds; using namespace __gnu_cxx; __gnu_pbds::priority_queue<int> pq[100005]; int n,m,fa[100005]; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } inline void merge(int x,int y){ int u=find(x),v=find(y); if(x!=y) fa[v]=u; } int main(){ ios::sync_with_stdio(0); cin>>n,iota(fa,fa+1+n,0); for(int i=1,t;i<=n;i++) cin>>t,pq[i].push(t); cin>>m; for(int i=1,x,y,t;i<=m;i++){ cin>>x>>y; t=pq[find(x)].top(),pq[find(x)].pop(),pq[find(x)].push(t/2); t=pq[find(y)].top(),pq[find(y)].pop(),pq[find(y)].push(t/2); pq[find(x)].join(pq[find(y)]),merge(x,y); cout<<pq[find(x)].top()<<endl; } return 0; }
|