食物链是并查集的进阶运用的一道非常经典的题目。
题目如下:
动物王国中有三类动物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),输出假话的总数。
题目的实质就是把给的N个动物划分到3个集合里去。最核心的在于需要实现父亲的父亲的父亲是同类。联想到取余操作。思路就出来了。
1 #include<cstdio> 2 using namespace std; 3 const int MAXN=50010; 4 int fa[MAXN]; 5 int rel[MAXN]; // 0代表同类,1代表吃fa[i],2代表被吃 6 void _set(int n) 7 { 8 for(int i=1;i<=n;i++) 9 { 10 fa[i]=i; 11 rel[i]=0; 12 } 13 } 14 int _find(int k) 15 { 16 if(fa[k]!=k) 17 { 18 int t=fa[k]; 19 fa[k]=_find(fa[k]); 20 rel[k]=(rel[k]+rel[t])%3; 21 } 22 return fa[k]; 23 } 24 void _union(int x,int y,int d) 25 { 26 int fx=_find(x); 27 fa[fx]=y; 28 rel[fx]=(d-1-rel[x]+3)%3; 29 } 30 int relation(int x,int y,int root) 31 { 32 return (rel[x]-rel[y]+3)%3; 33 } 34 int main() 35 { 36 // freopen("in.txt","r",stdin); 37 // freopen("out.txt","w",stdout); 38 int n,d,x,y,fx,fy; 39 long int k,ans; 40 ans=0; 41 scanf("%d %ld",&n,&k); 42 _set(n); 43 for(long int i=1;i<=k;i++) 44 { 45 scanf("%ld %ld %ld",&d,&x,&y); 46 if(x>n||y>n) 47 { 48 ans++; //printf("1: %d %d %d ",d,x,y); 49 continue; 50 51 } 52 if(d==2&&x==y) 53 { 54 ans++; 55 // printf("2: %d %d %d ",d,x,y); 56 continue; 57 58 } 59 fx=_find(x); 60 fy=_find(y); 61 if(fx!=fy) 62 { 63 _union(x,y,d); 64 } 65 else 66 { 67 if(d-1!=relation(x,y,fx)) 68 { 69 ans++; 70 // printf("3: %d %d %d %d ",d,x,y,relation(x,y,fx)); 71 } 72 } 73 } 74 printf("%ld",ans); 75 return 0; 76 }