动物王国中有三类动物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),输出假话的总数。
Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3
(参考博客,谢谢大佬,自己再次写了一下自己的一点儿理解)
https://blog.csdn.net/ShiWaiGaoRen12345/article/details/51202250
菜鸟分析:
有三类动物A,B,C,构成食物链:A吃B, B吃C,C吃A。
输入:
第一行是两个整数N和K,以一个空格分隔
以下K行每行是三个正整数 D,X,Y,。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
输出:假话的数目
列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
代码:
三个数字代表三种关系(x是一个数字)
x表示x属于A类,
x+n表示x属于B类,
x+2*n表示x属于C类
合并关系,就直接相当于合并数字
#include<cstdio>
#include<iostream>
using namespace std;
int fa[150010];
int find(int x){
if(x==fa[x]){
return x;
}//x的father为自身
return fa[x]=find(fa[x]);//不是自身继续找
}
//same函数 判断x y是否是同一个father,即是否同类,同类就为同一个father
int same(int x, int y){
if(find(x)==find(y))
return 1;
return 0;
}
//bind函数将x y组合,即为同一个father
void bind(int x, int y){
int fx=find(x);
int fy=find(y);
if(fx!=fy)//两数不在同一集合
fa[fx]=fy;//合并 同类合并为同一根结点 .
//int z=find(x);//z为x的father
//fa[z]=find(y);//z的father为y的father,即x的father的father为y的father
}//组合
int main(){
int n, k, no, x, y;
int ans=0;
scanf("%d%d", &n, &k);
//开辟了3*n的数组
for(int i=1; i<=3*n; i++){
fa[i]=i;//未进行合并,所有数字属于不同的集合
}//初始化并查集
for(int j=1; j<=k; j++){
scanf("%d%d%d", &no, &x, &y);
if(x<1||x>n||y<1||y>n){
ans++;//当前的话中X或Y比N大,就是假话;正整数
continue;
}
if(no==1){
/* x y+n y+2*分别为A类,B类 ,C类所以不应该为同一类,这是前提条件
情况1 同类所分的三类不能同
same(x, y+n)=same(A, B)即x可以吃y
same(x, y+2*n)==same(A, C)*/
if(same(x, y+n)||same(x, y+2*n)){
ans++;
}//错误情况
//1 x y 同类,如果是对的则以下三种情况
else{
bind(x, y);//A类
bind(x+n, y+n);//B类
bind(x+2*n, y+2*n);//C类
}//正确情况
}
//2 x y,表示X吃Y
else{
/*情况2有两种错误情况 1.X吃X同类吃 2. A吃C不符合食物链
1.same(x, y)=same(A, A)2 A A (X吃X,假话);
2.same(x, y+2*n)=same(A, C)2 A C(A吃C,假话)*/
if(same(x, y)||same(x, y+2*n)){
ans++;
}//错误情况
else{
bind(x, y+n);//A吃B
bind(x+n, y+2*n);//B吃C
bind(x+2*n, y);//C吃A
}//正确情况
}
}
printf("%d\n", ans);
return 0;
}
代码补充说明:刚开始是代表A B C代表吃的顺序,后来由于unite函数,A B C的关系从绝对关系由于unite函数变成了相对关系
若1 x y是错误的直接看是有same(x, y+n)或same(y+2*n, x),same这边表示两个元素在同一个集合。
same(x, y+n)表示x可以吃y;同理same(y+2*n, x)表示y可以吃x;
因为unite的操作,一旦有x吃y,接着所有的情况也被包括了。
bind(x, y+n);//A吃B
bind(x+n, y+2*n);//B吃C
bind(x+2*n, y);//C吃A
所以same(x, y+n)其实同时也包含了same(x+n, y+2*n)以及
same(y+2*n, x);就是等于同时把所有情况也都举了。
案例分析:
1.手动
100 7
1 101 1(X或Y比N大,就是假话)❌ x>n ans++
2 1 2 (1吃2) bind(1,2)
2 2 3 (2吃3) bind(2,3)
2 3 3 (X吃X,就是假话)❌ same(3,3) same(x,y) ans++
1 1 3 (1 3同类)❌ same(1,3) same(x,y+2*n) ans++
2 3 1 (3吃1符合食物链) bind(3,1) 适合食物链
1 5 5(5 5同类) bind(5,5)
所以假话3
不加注释代码:
#include<cstdio>
#include<iostream>
using namespace std;
int fa[150010];
int find(int x){
if(x==fa[x]){
return x;
}
return fa[x]=find(fa[x]);
}
int same(int x, int y){
if(find(x)==find(y))
return 1;
return 0;
}
void bind(int x, int y){
int fx=find(x);
int fy=find(y);
if(fx!=fy)
fa[fx]=fy;
int main(){
int n, k, no, x, y;
int ans=0;
scanf("%d%d", &n, &k);
for(int i=1; i<=3*n; i++){
fa[i]=i;
}
for(int j=1; j<=k; j++){
scanf("%d%d%d", &no, &x, &y);
if(x<1||x>n||y<1||y>n){
ans++;
continue;
}
if(no==1){
if(same(x, y+n)||same(x, y+2*n)){
ans++;
}
else{
bind(x, y);
bind(x+n, y+n);
bind(x+2*n, y+2*n);
}
}
else{
if(same(x, y)||same(x, y+2*n)){
ans++;
}
else{
bind(x, y+n);
bind(x+n, y+2*n);
bind(x+2*n, y);
}
}
}
printf("%d\n", ans);
return 0;
}