并查集
并查集
并查集朴素实现:
inline ll find(ll x)
{
if(fa[x]==x) return x;
return find(fa[x]);
}
路径压缩
inline ll find(ll x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
在朴素实现的过程中,复杂度较高,而路径压缩的复杂度为 \(O(α(n))\) ,可近似认为其复杂度为常数级别
但是路径压缩的做法是把所有的子节点都直接与根节点相连,会破坏树的原始结构
拓展域
对于多种关系的处理,我们可以将其合并范围增大,以表示不同的合并关系
- 例:食物链
将 \(1\) ~ \(n\) 范围表示A类,\(n+1\) ~ \(2n\) 范围表示B类,\(2n+1\) ~ \(3n\) 范围表示C类
之后按照题目的食物链关系合并即可
code
#include<iostream>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<algorithm>
#define ll long long
const ll maxn=5e4+10;
ll fa[3*maxn];
ll n,k,a,b,ans;
inline ll find(ll x)
{
if(fa[x]==x) return x;
else return fa[x]=find(fa[x]);
}
int main(void)
{
scanf("%lld %lld",&n,&k);
for(int i=1;i<=n*3;i++) fa[i]=i;
for(int i=1;i<=k;i++)
{
ll op,x,y;
scanf("%lld %lld %lld",&op,&x,&y);
if(x>n||y>n)
{
ans++;
continue;
}
if(op==1)
{
if(find(x+n)==find(y)||find(x+n+n)==find(y)) ans++;
else
{
fa[find(x)]=find(y);//同类
fa[find(x+n)]=find(y+n);//同类
fa[find(x+n+n)]=find(y+n+n);//同类
}
}
if(op==2)
{
if(x==y)
{
ans++;
continue;
}
if(find(x)==find(y)||find(x)==find(y+n+n)) ans++;
else
{
fa[find(x+n)]=find(y+n+n);//B->C
fa[find(x)]=find(y+n);//A->B
fa[find(x+n+n)]=find(y);//C->A
}
}
}
printf("%lld\n",ans);
return 0;
}
边带权
即在路径压缩的基础上,在进行合并之前,先对边权进行一些处理,之后再进行合并的操作
- 例:银河英雄传说
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long
using namespace std;
const ll maxn=3e4+10;
ll t,x,y;
char op;
ll fa[maxn],dis[maxn],siz[maxn];
inline ll find(ll x)
{
if(fa[x]==x) return x;
ll rot=find(fa[x]);
dis[x]+=dis[fa[x]];
return fa[x]=rot;
}
inline void upd(ll x,ll y)
{
ll ex=find(x),ey=find(y);
fa[ex]=ey;
dis[ex]=siz[ey];
siz[ey]+=siz[ex];
}
int main(void)
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
for(int i=1;i<=maxn;i++) fa[i]=i,siz[i]=1;
memset(dis,0,sizeof(dis));
cin>>t;
while(t--)
{
cin>>op>>x>>y;
if(op=='M')
{
upd(x,y);
}
if(op=='C')
{
ll ex=find(x),ey=find(y);
if(ex!=ey)
{
cout<<"-1"<<'\n';
continue;
}
else
{
if(x==y)
{
cout<<"0"<<'\n';
continue;
}
cout<<abs(dis[x]-dis[y])-1<<'\n';
}
}
}
return 0;
}