1. 程式人生 > 其它 >食物鏈(並查集)

食物鏈(並查集)

題目

動物王國中有三類動物 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 句話有的是真的,有的是假的。
當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
當前的話與前面的某些真的話衝突,就是假話;
當前的話中 X 或 Y 比 N 大,就是假話;
當前的話表示 X 吃 X,就是假話。
你的任務是根據給定的 N 和 K 句話,輸出假話的總數。

輸入輸出

輸入:第一行是兩個整數 N 和 K,以一個空格分隔。
以下 K 行每行是三個正整數 D,X,Y,兩數之間用一個空格隔開,其中 D 表示說法的種類。
若 D=1,則表示 X 和 Y 是同類。
若 D=2,則表示 X 吃 Y。
輸出:只有一個整數,表示假話的數目。

思路

三種被吃關係構成一個環,可以用並查集來維護每個點到根節點的距離。
距離模3餘0表示與根節點是同類,距離模3餘1表示被根節點吃的一類,距離模3餘2表示吃根節點的一類。

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 50010;
int p[N],d[N];  //d[i]一開始存的是i到父節點的距離,最後存的是第i個點到根節點的距離

int find(int x){    //路徑壓縮 + x到根節點的距離
    if(p[x] != x){
        int t = find(p[x]);
        d[x] += d[p[x]];
        p[x] = t;
    }
    return p[x];
}

int main()
{
    int n,m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )   p[i] = i;
    int res = 0;    //假話的數量
    while (m -- ){
        int c;  //每次說法的種類
        int x,y;
        cin >> c >> x >> y;
        if(x > n || y > n)  res ++;
        else{
            int px = find(x), py = find(y); //記錄x和y的根節點
            if(c == 1){ //x和y是同類
                if(px == py && (d[x] - d[y]) % 3){   //x和y屬於一個集合中了,但是模3的結果不同,就是假話
                    res ++;
                } 
                else if(px != py){   //x和y不屬於一個集合,就把x併到y的集合
                    p[px] = py;
                    d[px] = d[y] - d[x];
                }
            }
            else{   //x吃y,x模3比y模3大1
                if(px == py && (d[x] - d[y] - 1) % 3)   res ++; //在一個集合中,但不滿足x吃y關係
                else if(px != py){   //不在一個集合中
                    p[px] = py;
                    d[px] = d[y] - d[x] + 1;
                }
            }
        }
    }
    cout << res;
    return 0;
}
作者:inss!w! 出處:https://www.cnblogs.com/Hfolsvh/ 版權宣告:本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!