並查集------E - 食物鏈
動物王國中有三類動物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
這道題是個好題。。。。。
既然要求假話,肯定是說的話跟以前說的存在矛盾,比如A->B,B->A,這樣很明顯第二句話是假話了,跟第一句話衝突,那麼我們應該怎麼去查詢這種衝突呢,我們知道查詢兩個節點之間關係最快的方法就是並查集,只要把一些節點歸為一個集合,查詢起來是非常方便的,那麼這道題是不是也可以這麼做呢?答案是肯定的,不過我們除了要記錄每個節點的根節點以外,還要記錄與根節點的關係。
這個部落格講的非常明白了:https://www.cnblogs.com/liuxin13/p/4668205.html
生物之間的關係,0代表二者是同類,1代表A吃B,2代表A被B吃
產生關係(認定為正確的話)的兩個動物就進行合併,查詢的時候,兩個的根節點不相同,說明未產生關係,所以將兩個合併;如果兩個根節點相同,說明這兩個動物之間有關係,所以需要判斷是否說的錯的,如果(r[y] + d - 1)% 3 != r[x]。。。則說明這個說的是錯的,具體原因請看上面部落格。。。。。。
主要是並查集如何路徑壓縮,以及合併,路徑壓縮時用遞迴的寫法好,主要是計算路徑上的r值,合併是合併x與y的根節點即可。。。。
找出來他們的根節點ra 和 rb
我們想求出來 ra->rb ??
根據向量加法我們得出來
ra->rb = (ra->b+b->rb) % 3
ra->b = (a->b – a->ra + 3) % 3
合併一下兩個公式得出來
ra->rb = (a->b - a->ra + 3 + b->rb) %3
有了這個關係我們就可以合併兩個集合了,當然在做路徑壓縮的時候也不能忘了關係壓縮。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<queue> #include<cmath> using namespace std; typedef long long LL; const int inf = 0x3f3f3f3f; const double eps = 1e-8; const double PI = acos(-1); #define pb push_back #define mp make_pair #define fi first #define se second const int N = 50005; int b[N]; //記錄與根節點的關係 int r[N]; int join1(int x) { int k = b[x]; if(b[x] != x){ b[x] = join1(b[x]); r[x] = (r[x] + r[k]) % 3; } return b[x]; } int main() { int n,k; scanf("%d %d",&n,&k); int d,x,y; for(int i = 1;i <= n;++i){ b[i] = i; r[i] = 0; } int sum = 0; for(int i = 0;i < k;++i){ scanf("%d %d %d",&d,&x,&y); int p = join1(x),q = join1(y); if(x > n || y > n || (d == 2 && x == y)){ sum++; //p == q代表p與q已經產生關係了 }else if(p == q && (r[y] + d - 1) % 3 != r[x]){ sum++; }else if(p != q){ b[p] = q; r[p] = (d - 1 - r[x] + r[y] + 3) % 3; } } printf("%d\n",sum); return 0; }