动物王国中有三类动物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个动物,以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),输出假话的总数。
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
只有一个整数,表示假话的数目。
Sample Input
100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5Sample Output
3
学习带权并查集。
代码:
#include <iostream> #include <cstring> #include <cstdio> #include <map> #define Max 50005 using namespace std; int n,k,f[Max],tag[Max],c; ///f记录双亲结点 tag记录与双亲结点关系 0表示同类 1表示吃该结点 2表示被该结点吃 ///e.g. 1吃2 tag[1] = 0(初始和自己同类),tag[2] = 1;2吃3 tag[2] = 1,tag[3] = 1,getf之后 tag[3] = 2,因为3吃1;34同类 tag[3] = 2,tag[4] = 0,getf之后tag[4] = 2 ///现在tag[1] = 0,tag[2] = 1,tag[3] = 2,tag[4] = 2 ///此时判断 1吃2 (tag[2] - tag[1] + 3) % 3 = 1 满足;判断4吃2 (tag[2] - tag[4] + 3) % 3 = 2,结果是4被2吃 ///观察如果两个点x,y是同一个双亲,列举xy和双亲的关系 ///1.xy都被吃tag[x] = 1,tag[y] = 1,此时xy同类计算针对于x的tag'[y]应该是 0 满足(tag[y] - tag[x] + 3) % 3 = 0 ///2.x被吃 被y吃 tag[x] = 1,tag[y] = 2,y被x吃,tag'[y] = (tag[y] - tag[x] + 3) % 3 = 1 (两个交换类似) ///3.x同类 被y吃 tag[x] = 0,tag[y] = 2,y吃x,tag'[y] = (tag[y] - tag[x] + 3) % 3 = 2 (两个交换类似) ///4.x同类 y被吃 tag[x] = 0,tag[y] = 1,y被x吃, tag'[y] = (tag[y] - tag[x] + 3) % 3 = 1 (两个交换类似) ///5.xy都同类显而易见 ///6.被xy都吃,xy也是同类的 void init() { for(int i = 1;i <= n;i ++) { f[i] = i;///初始双亲是自己 } } int getf(int a)///寻根压缩 { if(f[a] != a) { int t = getf(f[a]);///如果直接让f[a] = 根 下一步就没意义了,tag[根] = 0 tag[a] = (tag[a] + tag[f[a]]) % 3;///根据a和双亲的关系与双亲和根的关系 转化为a和根的关系(规律很容易发现) f[a] = t; } return f[a]; } int mer(int d,int a,int b)///d = 0 同类 d = 1 a吃b { int aa = getf(a); int bb = getf(b); if(aa == bb)///表示在同一条食物链中 { if((tag[b] - tag[a] + 3) % 3 != d)return 0; return 1; } tag[bb] = (tag[a] - tag[b] + 3 + d) % 3; f[bb] = aa; return 1; } int main() { int d,a,b; scanf("%d%d",&n,&k); init(); for(int i = 0;i < k;i ++) { scanf("%d%d%d",&d,&a,&b); if(a > n || b > n || !mer(d - 1,a,b))c ++;///超过范围是假话 与原话不符或者与事实不符 } printf("%d",c); }