网上大多数题解都是用的带权并查集方法,这个方法就不详细说了。
这里介绍另一种方法,个人觉得理解起来更容易。
首先,考虑如何保存动物之间的关系,自然想到用数组 eat[i] = j 来表示 i 吃 j 的关系,用并查集来保存已经确定是同类的动物。
但光是这样远远不够,我们还需要用三角环形来表示动物之间的关系。
******************
1
2 3
******************
这个三角形就表示 1 吃 3 ,3 吃 2 , 2 吃 1。
******************
4
5 6
******************
这个三角形表示 4 吃 6 ,6 吃 5 , 5 吃 4。
如果来了新关系 1 和 4 是同类 ,我们只需要把两个三角形合并成一个三角形:
******************
1,4
2,5 3,6
******************
这时候只需要用并查集把同类动物保存起来就行了,派每个并查集的祖宗放在三角形里。
但是一开始是没有三角形的,为了代码简单一点可以人为初始化生成三角形:
****************************
i
i+100000 i+50000
****************************
这样的话一开始就全是三角形,写代码只需要写三角形合并代码就行了。
思想大致就是这样,其他细节自行思考。
#include<cstdio>
#include<algorithm>
using namespace std;
int fa[50005*3],eat[50005*3];
int f(int x){
if(fa[x]!=x)
fa[x] = f(fa[x]);
return fa[x];
}
void link(int a,int b){ //三角形合并,a和b是同类
int tfa1=f(a),tfa2=f(b),Eat,n1,n2;
for(int i=0;i<3;i++) {
Eat = min(eat[tfa1],eat[tfa2]);
n1 = f(eat[tfa1]);
n2 = f(eat[tfa2]);
if(tfa2<tfa1) fa[tfa1] = tfa2;
else fa[tfa2] = tfa1;
eat[tfa2] = eat[tfa1] = Eat;
tfa1 = n1;tfa2 = n2;
}
return;
}
int main(){
int n,k,d,a,b,t,ta,tb,ans;
scanf("%d%d",&n,&k);
ans = 0;
for(int i=1;i<=100000;i++) {
eat[i]=i+50000;
fa[i] = i;
}
for(int i=100001;i<=150000;i++) {
eat[i]=i-100000;
fa[i] = i;
}
for(int i=0;i<k;i++){
scanf("%d%d%d",&d,&a,&b);
if(a>n||b>n) ans++;
else if(d==1)
if(f(eat[f(a)])==f(b)||f(eat[f(b)])==f(a)) ans++;
else link(a,b);
else if(d==2)
if(f(a)==f(b)||f(eat[f(b)])==f(a)) ans++;
else link(eat[f(a)],b);
}
printf("%d\n",ans);
return 0;
}