POJ食物鏈(經典種類並查集)
阿新 • • 發佈:2018-12-13
食物鏈
動物王國中有三類動物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; }