1. 程式人生 > >POJ食物鏈(經典種類並查集)

POJ食物鏈(經典種類並查集)

食物鏈

動物王國中有三類動物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

 

思路:關於這道題我們運用到了一種特殊的技巧,動物的關係我們大概可以分為3種,動物本生,獵物,天敵,我們用三倍的並查集來維護這個關係,接下來的東西就看程式碼,感覺用程式碼來講解比較詳細,感覺寫的也算是中規中矩

// luogu-judger-enable-o2
#include<iostream>
using namespace std;
int father[150010];
int n, m;
//並查集模板
int findFather(int x) {
    while (x != father[x]) {
        x = father[x];
    }
    return x;
}

void Union(int a, int b) {
    int faU = findFather(a);
    int faV = findFather(b);
    if (faU != faV) {
        father[faU] = faV;
    }
}
int main() {
    int a, b, c, ans = 0;
    cin >> n >> m;
    for (int i = 1; i <= 3 * n; i++) father[i] = i;//用三倍來維護並查集,其中i為本身,i+n為獵物,i+2n為天敵
    for (int i = 0; i < m; i++) {
        cin >> a >> b >> c;
        if ((b > n || c > n) || (a == 2 && b == c)) {//先來一個特判,可以省時間
            ans++;
            continue;
        } else if (a == 1) {//如果他們是同一類
            if (findFather(b) == findFather(c + n) || findFather(b) == findFather(c + 2*n)) {//如果前者是後者的獵物或者天敵,說明他們不是同一類,則該句是謊言
                ans++;
                continue;
            }
            Union(b, c);//合併該類
            Union(b + n, c + n);//合併該類的獵物
            Union(b + 2 * n, c + 2 * n);//合併該類的天敵
        } else if (a == 2) {
            if (findFather(b) == findFather(c) || findFather(b) == findFather(c + n)) {//如果前者和後者是同一類或者前者是後者的獵物,說明該句是謊言
                ans++;
                continue;
            }
            Union(b, c + 2 * n);//合併該類和他獵物的天敵(敵人的敵人就是朋友)
            Union(b + n, c);//合併的獵物和後者(因為後者也是他的獵物)
            Union(b + 2 * n, c + n);//合併該類的天敵和後者的獵物(因為是3者相互剋制關係,稍微想想就明白了)
        }
    }
    cout << ans << endl;
    return 0;
}