题意:
三类动物A、B、C,A吃B,B吃C,C吃A。
给出K句话来描述N个动物(各属于A、B、C三类之一)之间的关系,格式及意义如下:
1 X Y:表示X与Y是同类;
2 X Y:表示X吃Y。
K句话中有真话有假话,当一句话满足下列三条之一时,这句话就是假话,否则就是真话。1)当前的话与前面的某些真的话冲突,就是假话;2)当前的话中X或Y比N大,就是假话;3)当前的话表示X吃X,就是假话。
求假话的总数。
输入:
第一行是两个整数N和K,以一个空格分隔。以下K行每行是三个正整数D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。若D=1,则表示X和Y是同类。若D=2,则表示X吃Y。
输出:
只有一个整数,表示假话的数目。
约束条件:
1 <= N <= 50000,0 <= K <= 100000。
思路:
并查集中的元素并不是动物,而是每只动物属于某种类别的信息。
具体来说,对于每只动物i创建i-A,i-B,i-C,并用这3*N个元素建立并查集。i-x表示“i属于种类x”。并查集里的每一个组内表示所有元素代表的情况同时发生或不发生。
对于x和y属于同一种类这种信息,合并x-A和y-A,x-B和y-B,x-C和y-C;
对于x吃y这种信息,合并x-A和y-B,x-B和y-C,x-C和y-A。
合并之前检查是否矛盾并计数即可。
实现:
1 #include <iostream> 2 #include <cstdio> 3 #define MAXN 50000 4 using namespace std; 5 int a[MAXN + 10]; 6 int par[3 * MAXN + 10]; 7 int ran[3 * MAXN + 10]; 8 void init(int n) 9 { 10 for (int i = 0; i <= n; i++) 11 { 12 par[i] = i; 13 ran[i] = 0; 14 } 15 } 16 int find(int x) 17 { 18 if (x == par[x]) 19 return x; 20 return par[x] = find(par[x]); 21 } 22 void unite(int x, int y) 23 { 24 x = find(x); 25 y = find(y); 26 if (x == y) 27 { 28 return; 29 } 30 if (ran[x] < ran[y]) 31 { 32 par[x] = y; 33 } 34 else 35 { 36 par[y] = x; 37 if (ran[x] == ran[y]) 38 { 39 ran[x]++; 40 } 41 } 42 } 43 bool same(int x, int y) 44 { 45 return find(x) == find(y); 46 } 47 int main() 48 { 49 int n, k, t, x, y; 50 cin >> n >> k; 51 int cnt = 0; 52 init(3 * n); 53 for (int i = 0; i < k; i++) 54 { 55 scanf("%d %d %d", &t, &x, &y); 56 if (x <= 0 || x > n || y <= 0 || y > n) 57 { 58 cnt++; 59 continue; 60 } 61 x--; 62 y--; 63 if (t == 1) 64 { 65 if (same(x + n, y + 2 * n) || same(x + n, y)) 66 { 67 cnt++; 68 } 69 else 70 { 71 unite(x, y); 72 unite(x + n, y + n); 73 unite(x + 2 * n, y + 2 * n); 74 } 75 } 76 else 77 { 78 if (same(x + n, y) || same(x + 2 * n, y + 2 * n)) 79 { 80 cnt++; 81 } 82 else 83 { 84 unite(x, y + n); 85 unite(x + n, y + 2 * n); 86 unite(x + 2 * n, y); 87 } 88 } 89 } 90 cout << cnt << endl; 91 return 0; 92 }