关于并查集和路径压缩:
现在我们假定 f[i] 表示第 i 个人的老大是谁。
现在我们有甲,乙,丙三个人(分别用 a, b, c 表示)
假设甲和乙打架了,甲做了乙的小弟。则有 f[a]=b,
后来甲打赢了丙,那么丙就是甲的小弟了。有 f[c]=a,
但是如果我们这样表示,丙不能直接知道甲,容易自己人打自己人
所以,我们必须直接让丙的大哥变成最大的老大。
定义函数 find
int find(int k){
if(f[k]==k)return k;
return find(f[k]);
}//find 函数可以直接找到最大的老大
f[c]=find(a);
//丙的老大是甲
这时,因为我们要路过他所有的上级,我们也可以顺便使途中经过的人的大哥也变成老大。
//路径压缩
int find(int k){
if(f[k]==k)return k;
return f[k]=find(f[k]);
/*
即:
f[k]=find(f[k]);
return f[k];
*/
}
f[c]=find(a);
简直是太巧妙了!
而判定两个人的老大是否相等,只需下面这样就好了:
if(find(a)==find(b))
一些设定:
一个人不能有两个老大。
当已经有老大的人臣服时,老大也将成为胜利的人的小弟。
所以本题代码为:
#include<bits/stdc++.h>
using namespace std;
int f[100002];//f[i]表示i的集合名
int find(int k){
//路径压缩
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
int main()
{
int n,m;
int z,x,y;
cin>>n>>m;
for(int i=1;i<=n;i++)
f[i]=i;//初始化i的老大为自己
for(int i=1;i<=m;i++){
cin>>z>>x>>y;
if(z==1)
f[find(x)]=find(y);
//p3打赢了p2
else{
if(find(x)==find(y))
//是否是一伙的
printf("Y\n");
else
printf("N\n");
}
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int f[50002];
int find(int k){
if(f[k]==k)
return k;
else
return f[k]=find(f[k]);
}
int main()
{
int n,m,p;
int mi,mj;
int pi,pj;
cin>>n>>m>>p;
for(int i=0;i<n;i++){
f[i]=i;
}
for(int i=0;i<m;i++){
cin>>mi>>mj;
f[find(mi)]=find(mj);
}
for(int i=0;i<p;i++){
cin>>pi>>pj;
if(find(pi)==find(pj)){
cout<<"Yes"<<endl;
}
else
cout<<"No"<<endl;
}
return 0;
}