​传送门​

题目大意

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

带权并查集思路

POJ 1182 食物链(带权并查集||种类并查集)_带权并查集代表这个点与根节点的关系,POJ 1182 食物链(带权并查集||种类并查集)_带权并查集_02 同类 POJ 1182 食物链(带权并查集||种类并查集)_父节点_03 吃父节点 POJ 1182 食物链(带权并查集||种类并查集)_带权并查集_04 被父节点吃
查找(POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_05)
POJ 1182 食物链(带权并查集||种类并查集)_带权并查集_06可以画图推一下
合并(POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_07)
父节点相同时,关系成立的条件是POJ 1182 食物链(带权并查集||种类并查集)_父节点_08
父节点不同时,合并父节点,并更新x父节点的POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_09值,POJ 1182 食物链(带权并查集||种类并查集)_父节点_10
不理解怎么更新的可以画图看一下

带权并查集代码

struct node{
int fa;//父节点
int rel;//与父节点的关系 1同类 2吃父节点 3被父节点吃
}str[maxn];

int find(int x){
if(str[x].fa==x){
return x;
}
int tmp=find(str[x].fa);
str[x].rel=(str[x].rel+str[str[x].fa].rel)%3;//从祖先节点向后开始计算x的rel
return str[x].fa=tmp;//路径压缩
}

int Union(int op,int x,int y){
int fx=find(x);
int fy=find(y);
if(fx==fy){//是一样的根节点
if((str[x].rel-str[y].rel+3)%3==op-1)
return 0;//关系正确
return 1; //给出关系与上面的矛盾
}
str[fx].fa=fy;
str[fx].rel=(-str[x].rel+op-1+str[y].rel+3)%3;//更新父节点的rel
return 0;
}

int main(){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){//初始化
str[i].fa=i;
str[i].rel=0;
}
int ans=0;
int op,x,y;
while(k--){
scanf("%d%d%d",&op,&x,&y);
if(x==y&&op==2)
ans++;
else if(x>n||y>n)
ans++;
else
ans+=Union(op,x,y);
}
printf("%d\n",ans);
}

种类并查集思路

POJ 1182 食物链(带权并查集||种类并查集)_父节点_11代表同类,POJ 1182 食物链(带权并查集||种类并查集)_带权并查集_12代表天敌,POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_13代表食物
POJ 1182 食物链(带权并查集||种类并查集)_父节点_14时:POJ 1182 食物链(带权并查集||种类并查集)_父节点_11的猎物与POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_16的天敌若是一样则POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_17不能是同类,同理POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_16的猎物与POJ 1182 食物链(带权并查集||种类并查集)_父节点_11的天敌一样,POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_17也不能是同类,那么这句话就是假话。若这句话是真话,就合并POJ 1182 食物链(带权并查集||种类并查集)_父节点_21.
POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_22时:POJ 1182 食物链(带权并查集||种类并查集)_父节点_11POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_16在一个集合,假话,POJ 1182 食物链(带权并查集||种类并查集)_父节点_11的天敌是POJ 1182 食物链(带权并查集||种类并查集)_种类并查集_16,假话。若这句话是真话,合并POJ 1182 食物链(带权并查集||种类并查集)_带权并查集_27

种类并查集代码

int fa[maxn];/// x 本身  x+n 天敌   x+2n食物 

int find(int x){
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}

void Union(int x,int y){
fa[find(x)]=find(y);
}

int n,m;

int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n*3;i++){
fa[i]=i;
}
int op,x,y;
int ans=0;
while(m--){
scanf("%d%d%d",&op,&x,&y);
if(x>n||y>n){
ans++;
continue;
}
/// x 本身 x+n 天敌 x+2n食物
if(op==1){//同类
if(find(x+2*n)==find(y+n)||find(x+n)==find(y+2*n)){
ans++;
}
else{
Union(x,y);
Union(x+n,y+n);
Union(x+2*n,y+2*n);
}
}
else{//x吃y
if(find(x+n)==find(y)||find(x)==find(y)){
ans++;
}
else{
Union(x+2*n,y);
Union(y+2*n,x+n);
Union(y+n,x);
}
}
}
printf("%d\n",ans);
}