食物鏈(並查集的簡單應用)

npufz發表於2014-07-25

題目來源:[NWPU][2014][TRN][12]並查集 C 題

http://vjudge.net/contest/view.action?cid=50731#problem/C

作者:npufz

題目:

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

程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
int parent[50003];
int real[50003],n;
int getparent(int a)
{
    if(a==parent[a])
        return a;
        int t;
    t=getparent(parent[a]);
    real[a]=(real[parent[a]]+real[a])%3;
    //cout<<real[a]<<endl;
    parent[a]=t;
    return parent[a];
}
bool panduan (int d,int dw1,int dw2)
{
    int pre1,pre2;
    if(dw1>n||dw2>n||dw1<1||dw2<1||d>2||d<1) return false ;
    if(d==2&&dw1==dw2) return false;
    if(d==1&&dw1==dw2) return true;
    pre1=getparent(dw1);
    pre2=getparent(dw2);
    if(d==1)
    {
        if(dw1==dw2) return true;
        if(dw1!=dw2)
        {
            if(pre1==pre2)
            {
                if(real[dw1]==real[dw2]) return true;
                return false;
            }
            if(pre1!=pre2)
            {
              if(real[dw1]>=real[dw2])
              {
                  parent[pre2]=pre1;
                  real[pre2]=(real[dw1]-real[dw2]);
                  return true;
              }
              else
              {
                  parent[pre1]=pre2;
                  real[pre1]=(real[dw2]-real[dw1]);
                  return true;
              }
            }
        }
    }
    if(d==2)
    {
        if(pre1==pre2)
        {
            if((real[dw2]-real[dw1])==1)  return true;
            if((real[dw1]-real[dw2]==2))  return true;
            return false ;
        }
        if(pre1!=pre2)
        {
            if(real[dw1]==real[dw2])
                {
                parent[pre2]=pre1;
                real[pre2]=1;
                return true;
                }
            if((real[dw1]+1)>=real[dw2])
            {
                //cout<<"ok"<<real[pre2]<<endl;
                parent[pre2]=pre1;
                real[pre2]=((real[dw1]+1)-real[dw2])%3;
                //cout<<"ok"<<real[pre2]<<endl;
                return true;
            }
            if((real[dw1]+1)<real[dw2])
            {    //cout<<"not ok"<<real[pre2];
                parent[pre2]=pre1;
                real[pre2]=2;
                //cout<<"not ok"<<real[pre2];
                return true;
            }
        }
    }
return true;
}
int main()
{
    int k,d,dw1,dw2,wrong;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
      real[i]=0;
      parent[i]=i;
    }
    wrong=0;
    for(int i=0;i<k;i++)
     {
         scanf("%d%d%d",&d,&dw1,&dw2);
         if(!panduan(d,dw1,dw2))
            wrong++;
     }
    printf("%d\n",wrong);
return 0;
}
反思:並查集在於把元素的分類和關係的描述上進行合理的資料維護,用壓縮路徑或者是RANK值使複雜度控制在LOG N 以下,具體的實現形式不拘一格,在這一題中,我把動             物之間已知的關係構造出一顆顆的樹,也就是森林,當兩棵樹的元素有關係時,就把一棵樹的樹根連到另一棵樹根上,在每次進行對樹根的查詢時,就直接把這個葉子節             點連線到根節點上,並同時確定它和根節點的關係,一開始由於第81行的那個是2的值寫成了1,就WA了,還以為整個資料集合的處理有問題,就糾結了一個晚上也沒有             找出錯誤,後來找到就A了

相關文章