启发式合并

到现在才知道还哦有这个东西……


最言简意赅地介绍:

给定 \(n\) 个集合,每次指定两个集合进行元素合并,合并 \(n-1\) 次,返回最终的大集合。

暴力的合并就是一个一个集合的合并,这样就显然是 \(n^2\) 的复杂度。

启发式合并:每一次合并时将较小的集合放到较大的集合中,时间复杂度 \(O(n \log n)\)


典例:

观察到吧把 \(x\) 颜色改为 \(y\) 颜色实质上就是合并 \(x\)\(y\) 的过程,所以我们采用启发式合并,具体过程:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x7fffffff
const ll maxn=1000010;
ll now[maxn],color[maxn];
ll ans;
vector<ll> G[maxn];
inline void merge(ll x,ll y) {
	for (ll i=0;i<G[x].size();++i) {
		if (color[G[x][i]+1]==y) ans--;
		if (color[G[x][i]-1]==y) ans--;
	}
	for (ll i=0;i<G[x].size();++i) color[G[x][i]]=y;
	for (ll i=0;i<G[x].size();++i) G[y].push_back(G[x][i]);
	G[x].clear();
}
inline ll in() {
    char a=getchar();
	ll t=0,f=1;
	while(a<'0'||a>'9') {if (a=='-') f=-1;a=getchar();}
    while(a>='0'&&a<='9') {t=(t<<1)+(t<<3)+a-'0';a=getchar();}
    return t*f;
}
signed main() {
	ll n=in(),m=in();
	for (ll i=1;i<=n;++i) {
		color[i]=in();
		now[color[i]]=color[i];
		if (color[i]!=color[i-1]) ans++;
		G[color[i]].push_back(i);
	}
//	for (int i : G[9]) cout<<i<<" "; 
	for (ll i=1;i<=m;++i) {
		ll opt=in();
		if (opt==2) printf("%lld\n",ans);
		else {
			ll x=in(),y=in();
			if (x==y) continue;
			if (G[now[x]].size()>G[now[y]].size()) {
				merge(now[y],now[x]);
				swap(now[x],now[y]);
			}
			else merge(now[x],now[y]);
		}
	}
	return 0;
}

树上启发式合并

(没学述链剖分)候补……

posted @ 2023-07-10 02:28  Pwtking  阅读(25)  评论(0)    收藏  举报