set+并查集查无向图第k大的节点(启发式合并优化)
关于启发式合并https://oi-wiki.org/graph/dsu-on-tree/
在这里,对于两个大小不一样的集合,我们将小的集合合并到大的集合中,而不是将大的集合合并到小的集合中。
为什么呢?这个集合的大小可以认为是集合的高度(在正常情况下),而我们将集合高度小的并到高度大的显然有助于我们找到父亲。
让高度小的树成为高度较大的树的子树,这个优化可以称为启发式合并算法。
并查集的启发式合并
void merge(int x, int y) {
int xx = find(x), yy = find(y);
if (size[xx] < size[yy]) swap(xx, yy);
fa[yy] = xx;
size[xx] += size[yy];
}
题目
https://atcoder.jp/contests/abc372/tasks/abc372_e
#include<bits/stdc++.h>
#define endl '\n'
#define lowbit(x) (x&-x)
using namespace std;
const double pi=acos(-1);
const int N=3e5+5;
set<int> st[N];
int fa[N];
int n,q;
int find(int i){
if(i!=fa[i]) fa[i]=find(fa[i]);
return fa[i];
}
void solve(){
cin>>n>>q;
for(int i=1;i<=n;i++){
st[i].insert(i);fa[i]=i;
}
while(q--){
int op;cin>>op;
int u,v;cin>>u>>v;
u=find(u);
if(op==1){
v=find(v);
if(u>v) swap(u,v);//并查集的启发式合并
st[u].insert(v);
st[v].insert(u);
if(u!=v){
fa[v]=u;
for(set<int>::iterator x=st[v].begin();x!=st[v].end();x++){
st[u].insert(*x);
}
}
}
else{
if(v>st[u].size()){
cout<<-1<<endl;
}
else{
set<int>::iterator now=st[u].end();
for(int i=1;i<=v;i++,now--);
cout<<*now<<endl;
}
}
}
}
signed main(){
ios::sync_with_stdio(false); cin.tie(nullptr);
int t=1;
//cin>>t;
while(t--) solve();
return 0;
}