POJ 1182 食物链【并查集】
题意:
动物王国中有三类动物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),输出假话的总数。
分析: 每当出现一个新的关系,判断已有的关系是否和其冲突,如果不冲突,那么加入之后立即更新祖先和孩子的关系。
加入一个记录每个元素和祖先关系的数组 r[].
r[i] = 0 : 表示 i 和祖先是同一个物种。
r[i] = 1 : 表示 i 的祖先 吃 i 这个物种。
r[i] = 2 : 表示 i 吃自己的祖先。
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) #define INF 0x1f1f1f1f int f[500005]; int r[500005]; int n,m; int find(int x) { int s; if(f[x]==-1) return x; else s=find(f[x]); r[x]=(r[x]+r[f[x]])%3; f[x]=s; return s; } void join(int x,int y,int w) { int fx=find(x); int fy=find(y); f[fy]=fx; r[fy]=(r[x]-r[y]+w+3)%3; } int main() { scanf("%d%d",&n,&m); int res=0,a,b,w; clr(r); memset(f,0xff,sizeof(f)); while(m--) { scanf("%d%d%d",&w,&a,&b); if(a>n||b>n) { res++; continue; } else { int fx=find(a); int fy=find(b); if(fx==fy) { if(w==1&&r[a]!=r[b]) { res++; continue; } if(w==2&&((r[a]+1)%3!=r[b]||a==b)){ res++; continue; } } else join(a,b,w-1); } } printf("%d\n",res); return 0; }