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;
}

posted on 2024-10-15 17:05  TaopiTTT  阅读(3)  评论(0编辑  收藏  举报