动物王国中有三类动物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和K句话,输出假话的总数。
【输入格式】
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
【输出格式】
只有一个整数,表示假话的数目。
【输入样例】
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
【输出样例】
3
【数据范围】
1<=N<=50,000
0<=K<=100,000
【来源】
对输入的7句话的分析如下:
100 7
1 101 1 假话
2 1 2 真话
2 2 3 真话
2 3 3 假话
1 1 3 假话
2 3 1 真话
1 5 5 真话
解题思路:根据题意,可以运用并查集的思想来解决此题,对于每次输入的话,先判断输入的X与Y的关系是否已知,如果未知,则将X与Y存入并查集中,并注明X与Y的关系;如果已知,则判断已知关系与所输入的关系是否相同(注意,如果X>N或Y>N,则输入的话是假话)。本题的重点及难点在于,已知X的关系和Y的关系,如何将他们的关系合并,要具体情况具体分析,下面就本题出现的合并关系的情况一一进行分析。先设pa[i]==j(i的父亲是j),re[i]==0表示i与j是同类,re[i]==1表示i被j吃,re[i]==2表示i吃j。
①在寻找x所在集合的代表元(根),进行路径压缩时,将x的关系与pa[x](x的父亲)的关系进行合并。
通过上图,可以看出当x被pa[x]吃,而pa[x]吃root(根)时,x与root同类。同理,多列举几种可能,可以得出此时的关系合并的通式为re[x]=(re[x]+re[pa[x]])%3。
②在通过输入数据确定了x与y的关系后,将x的根与y的根(路径压缩后,即为他们的父亲)的关系进行合并。
通过上图,可以看出若输入的话为x吃y(y被x吃),而假设y与py是同类,x吃px,则px与py是同类。同理,多列举几种可能,设合并时py的父亲改为px,可以得出通式为re[py]=(re[x]-re[y]+d+3)%3(这里的d为根据输入得出的y对于x的关系)。
③在输入每句话,判断X与Y的关系是否已知,如果X与Y的关系未知(X与Y的根不同),则返回3,如果已知关系,则返回已知的Y与X的关系。(px、py分别为x和y的根,路径压缩后即为他们的父亲)(注,这里返回的是Y对于X的关系)
通过上图,可以看出若y被py吃,x吃px,y吃x。同理,多列举几种可能,可以得出通式返回的值为(re[y]-re[x]+3)%3。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=50005;
int N,K,d,x,y,ans=0;
int pa[maxn],re[maxn]; //设pa[i]==j(i的父亲是j),re[i]==0表示i与j是同类,re[i]==1表示i被j吃,re[i]==2表示i吃j
void initial() //并查集初始化
{
for(int i=1;i<=N;i++)
pa[i]=i,re[i]=0;
}
int find(int x)
{
if(pa[x]==x) return x;
int root=find(pa[x]);
re[x]=(re[x]+re[pa[x]])%3; //重点
pa[x]=root;
return root;
}
int judge(int x,int y) //0同类 1被吃 2吃 3不知道
{
int px=find(x);
int py=find(y);
if(px!=py) return 3;
return (re[y]-re[x]+3)%3; //重点,难点,返回的是y对于x的关系
}
void bing(int x,int y,int d) //这里的d为y对于x的关系
{
int px=find(x);
int py=find(y);
pa[py]=px;
re[py]=(re[x]-re[y]+d+3)%3; //重点,难点
}
int main()
{
//freopen("48.in","r",stdin);
//freopen("48.out","w",stdout);
scanf("%d%d",&N,&K);
initial();
for(int i=1;i<=K;i++)
{
scanf("%d%d%d",&d,&x,&y);
int t=judge(x,y);
if(x>N || y>N)
{
ans++;
continue;
}
if(t==3) //关系未知
{
bing(x,y,d-1);
continue;
}
if(t!=d-1) ans++; //因为返回的是y对于x的关系,这里才可以用t!=d-1来判断
}
printf("%d\n",ans);
return 0;
}