洛谷地址:https://www.luogu.com.cn/problem/P3367
并查集可以用以下一段话描述:
假设有
a
a
a、
b
b
b、
c
c
c三个人,原本各自为老大(我们设
f
[
i
]
f[i]
f[i]的值表示第
i
i
i个人的老大是谁,自身为老大则
f
[
i
]
=
i
f[i]=i
f[i]=i)
后来 a a a成了 c c c的小弟,则 f [ a ] = c f[a]=c f[a]=c
接着
a
a
a打败了
b
b
b,此时,我们不能再以原来的公式了,因为
f
[
b
]
=
a
f[b]=a
f[b]=a,并不能让
b
b
b明白真正的老大是
c
c
c,容易自己人打自己人,那么我们试着用第二种公式带入
f
[
b
]
=
f
[
a
]
=
c
f[b]=f[a]=c
f[b]=f[a]=c
这样是不是就够了呢,我们再假设有 a a a、 b b b、 c c c、 d d d四人, a a a是 b b b的小弟, c c c是 d d d的小弟,如果 a a a打败了 d d d,则 d d d的老大变成了 b b b,但 c c c的老大还是 d d d,因为最后判断是否同集,无法直接判断( i f ( f [ i ] = = f [ j ] ) if(f[i]==f[j]) if(f[i]==f[j])) c c c和 a 、 b 、 d a、b、d a、b、d是否同集,所以作出以下函数:
int Myfind(int q)//找老大
{
if(f[q]==q) return q;
return Myfind(f[q]);
}
既然要不停地找,为何不直接将途经的全部改变呢:
int Myfind(int q)//找老大
{
if(f[q]==q) return q;
return f[q]=Myfind(f[q]);
}//警告一下,不改成这样的话样例会有三道超时的
用递归来找出真正的老大,所以公式变成了这样: f [ M y f i n d ( i ) ] = M y f i n d ( j ) f[Myfind(i)]=Myfind(j) f[Myfind(i)]=Myfind(j)
当然,光是这样仍不能解决上面的问题,所以我们要改变判断方式—— i f ( M y f i n d ( i ) = = M y f i n d ( j ) ) if(Myfind(i)==Myfind(j)) if(Myfind(i)==Myfind(j))
最后,我们将这个故事带入题目,判断是否同集就是判断是否同一个老大,当
Z
i
=
1
Z_i=1
Zi=1时,我们当做
X
i
X_i
Xi打败了
Y
i
Y_i
Yi。此时这题就解决了,难点不在代码,而在理解。(当然函数还是重要的
A C AC AC C O D E : CODE: CODE:
#include <bits/stdc++.h>
using namespace std;
int n,k,f[1000010],x,y,z;
int Myfind(int q){//找老大
if(f[q]==q) return q;
return f[q]=Myfind(f[q]);
}//重点
int main() {
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++){//初识各自为老大
f[i]=i;
}
for(int i=0;i<k;i++){//继续输入并输出
cin>>z>>x>>y;
if(z==1){
f[Myfind(y)]=Myfind(x);//次重点
}else{
if(Myfind(x)==Myfind(y)){//次重点
printf("Y\n");
}else{
printf("N\n");
}
}
}
return 0;
}