解析:
没做过这道题的小伙伴可以去看一看。
那道题我偷懒记录了一下每个人的敌人(因为敌人的敌人是朋友是一个自反关系)。
这道题既不是自反关系也不是偏序关系,所以我们考虑拆点。
对一个点 x x ,我们分为三个点,对于任意的点 a a ,是它的天敌, a+n a + n 是的猎物。
则每次合并操作我们只需要将对应集合合并三次就行了。
判断的时候判断一下是否有不该在一个集合中的在一个集合里面了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
#define rank Rank
inline
ll getint(){
re ll num=0;
re char c;
while(!isdigit(c=gc()));
while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
return num;
}
inline
void outint(ll a){
static char ch[23];
if(a==0)pc('0');
while(a)ch[++ch[0]]=(a-a/10*10)^48,a/=10;
while(ch[0])pc(ch[ch[0]--]);
}
int fa[150003];
int Rank[150003];
inline
int getfa(int x){
return fa[x]==x?x:fa[x]=getfa(fa[x]);
}
inline
void merge(int i,int j){
i=getfa(i),j=getfa(j);
if(i==j)return ;
if(rank[i]>rank[j])swap(i,j);
fa[j]=i;
if(rank[i]==rank[j])++rank[i];
}
int n,k;
int ans;
signed main(){
n=getint();
for(int re i=1;i<=n*3;++i)fa[i]=i;
k=getint();
while(k--){
int op=getint();
int x=getint(),
y=getint();
if(x>n||y>n){
++ans;
continue;
}
switch(op){
case 1:{
if(x==y)continue;
if(getfa(x)==getfa(y+n)||getfa(x)==getfa(y+n*2)){//判断是否已经有天敌、猎物关系
++ans;
continue;
}
merge(x,y),merge(x+n,y+n),merge(x+n*2,y+n*2);
break;
}
case 2:{
if(x==y||getfa(x)==getfa(y)||getfa(x)==getfa(y+n*2)){//判断是否是同种,或者与描述相反的天敌、猎物关系
++ans;
continue;
}
merge(x,y+n),merge(x+n,y+n*2),merge(x+2*n,y);
break;
}
}
}
cout<<ans;
return 0;
}