启发式合并
到现在才知道还哦有这个东西……
最言简意赅地介绍:
给定 \(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;
}
树上启发式合并
(没学述链剖分)候补……