poj 1182 食物鏈(並查集

原文鏈接:https://blog.csdn.net/libing923/article/details/8240995
食物鏈
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 31929   Accepted: 9326

Description

動物王國中有三類動物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
分析:經典並查集
1.p[x]表示x根結點。r[x]表示p[x]與x關係。r[x]=0 表示p[x]與x同類;1表示p[x]吃x;2表示x吃p[x]。
2.怎樣劃分一個集合呢?
  注意,這裏不是根據x與p[x]是否是同類來劃分。而是根據“x與p[x]能否確定兩者之間關係”來劃分,若能確定x與p[x]關係,則它們同屬一個集合
3.怎樣判斷一句話是不是假話?
  假設已讀入D ,X ,Y ,先利用findset()函數得到X,Y所在集合代表元素fx,fy,若它們在同一集合(即fx==fy)則可以判斷這句話真僞:
        若 D==1 而 r[X]!=r[Y] 則此話爲假.(D==1 表示X與Y爲同類,而從r[X]!=r[Y]可以推出 X 與 Y 不同類.矛盾.)
        若 D==2 而 r[X]==r[Y](X與Y爲同類)或者r[X]==(r[Y]+1)%3(Y吃X)則此話爲假。
4.上個問題中r[X]==(r[Y]+1)%3這個式子怎樣推來?
      假設有Y吃X,那麼r[X]和r[Y]值是怎樣?
        我們來列舉一下: 
		           r[X]=0&&r[Y]=2 
                           r[X]=1&&r[Y]=0
                           r[X]=2&&r[Y]=1
          稍微觀察一下就知道r[X]=(r[Y]+1)%3;
      事實上,對於上個問題有更一般判斷方法:
           若(r[Y]-r[X]+3)%3!=D-1 ,則此話爲假.
5.其他注意事項:
       在Union(d,x,y)過程中若將S(fy)合併到S(fx)上,則相應r[fy]必須更新爲fy相對於fx關係。怎樣得到更新關係式?
       r[fy]=(r[x]-r[y]+d+3)%3;
  1. #include<cstdio>
  2. const int N=50001;
  3. int p[N],r[N],n;
  4. int findset(int x)
  5. {
  6. if(x!=p[x])
  7. {
  8. int fx=findset(p[x]);
  9. r[x]=(r[x]+r[p[x]])%3;
  10. p[x]=fx;
  11. }
  12. return p[x];
  13. }
  14. bool Union(int d,int x,int y)
  15. {
  16. int fx=findset(x),fy=findset(y);
  17. if(fx==fy)
  18. {
  19. if((r[y]-r[x]+3)%3!=d)return 1;
  20. else return 0;
  21. }
  22. p[fy]=fx;
  23. r[fy]=(r[x]-r[y]+d+3)%3;
  24. return 0;
  25. }
  26. int main()
  27. {
  28. int k,ans,i,d,x,y;
  29. scanf("%d%d",&n,&k);
  30. ans=0;
  31. for(i=1;i<=n;i++)p[i]=i,r[i]=0;
  32. while(k--)
  33. {
  34. scanf("%d%d%d",&d,&x,&y);
  35. if(x>n||y>n||(x==y&&d==2)){ans++;continue;}
  36. if(Union(d-1,x,y))ans++;
  37. }
  38. printf("%d\n",ans);
  39. return 0;
  40. }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章