1.点带权
初始化f[i]=i,num[i]=1,num[i]表示i点所在的集合的数量,需要注意的是,一个点所在的集合的点的数量保存在其根节点中
代码如下:
int f[maxn],num[maxn];
int find(int x){
if(x==f[x]) return x;
return f[x]=find(f[x]);
}
void vol(int x,int y){
int a=find(x);
int b=find(y);
if(a!=b){
f[a]=b;
num[b]+=num[a];
}
}
2.边带权
dis[i]表示点i到所在集合根节点的距离,其中边权为一。初始化f[i]=i
代码如下:
int f[maxn],dis[maxn];
int find(int x){
if(x==f[x]) return x;
int root=find(f[x]);
dis[x]+=dis[f[x]];
return f[x]=root;
}
void vol(int x,int y){
int a=find(x);
int b=find(y);
if(a!=b){
f[a]=b;
dis[x]=dis[y]+1;
}
}
3.例题
3.1 家族合并
题目描述:有 n 个人,刚开始每个人都代表着一个家族,现在要对其进行操作,一共有如下三种操作:
1: C a b,a 和 b 所在的家族合并到一起
2:Q1 a b,查询 a 和 b 是否在同一个家族
3:Q2 a,查询 a 所在的家族有多少个人
对于每个询问指令 Q1 a b,如果 a 和 b 在同一个家族中,则输出 Yes,否则输出 No。
对于每个询问指令 Q2 a,输出一个整数表示点 a 所在家族的人数
题解:
点带权,代码如下
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn=1e5+10;
int f[maxn],num[maxn];
int find(int x){
if(x==f[x]) return x;
return f[x]=find(f[x]);
}
void vol(int x,int y){
int a=find(x);
int b=find(y);
if(a!=b){
f[a]=b;
num[b]+=num[a];
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
f[i]=i;
num[i]=1;
}
for(int i=1;i<=m;i++){
string c;
cin>>c;
if(c=="C"){
int a,b;
cin>>a>>b;
vol(a,b);
}
if(c=="Q1"){
int a,b;
cin>>a>>b;
if(find(a)==find(b)) cout<<"Yes"<<"\n";
else cout<<"No"<<"\n";
}
if(c=="Q2"){
int a;
cin>>a;
cout<<num[find(a)]<<"\n";
}
}
return 0;
}
3.2 信息传递
有 n 个同学(编号为 1 到 n )正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 Ti 的同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息, 但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自 己的生日时,游戏结束。请问该游戏一共可以进行几轮
题解:
本题就是求解给定的图中所有环中的最小环的边数,在结合边带权
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn=2e5+10;
int f[maxn],dis[maxn],n,mn=INT_MAX;
int find(int x){
if(x==f[x]) return x;
int root=find(f[x]);
dis[x]+=dis[f[x]];
return f[x]=root;
}
void vol(int x,int y){
int a=find(x);
int b=find(y);
if(a==b){//若以连接,则更新最小环长度
mn=min(mn,dis[x]+dis[y]+1);
}
f[a]=b;
dis[x]=dis[y]+1;//若未连接,则联通两点,更新父亲节点和路径长
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
f[i]=i;
}
for(int i=1;i<=n;i++){
int x;
cin>>x;
vol(i,x);
}
cout<<mn<<"\n";
return 0;
}