題目網址 :POJ -- 1182ios
這是一道利用並查集模擬的題目,也是並查集的裸題。spa
乍一看好像有點麻煩,由於會以爲動物們來回吃來吃去狀況就會變得愈來愈複雜。code
其實不管X和Y是同類仍是捕食關係,歸根結底都是集合合併的問題(捕食關係「錯位」一下也是對等合併)。ci
考慮何時會發生衝突?衝突的狀況只有一個,那就是兩個有交集的食物鏈合併,而且合併的時候發生錯位,也就是ABC不對等合併。另外的狀況都是元素X所在的食物鏈和元素Y所在的食物鏈沒有交集,這樣合併後更新就好。get
舉個例子,就是你能夠ABC和ABC合併, 也能夠ABC和DEF合併,可是絕對不能ABC和BCA或者ACB合併。 緣由是你如今獲取了兩個元素X和Y,他們倆要麼處於同一個食物鏈中,要麼處於不一樣食物鏈中,不一樣食物鏈能夠直接合並,相同食物鏈只能對等合併(其實也就是不用合併),不能錯位合併。io
並查集是一種樹結構,有一個特別典型的特性:只要不是根節點,就先獲取根節點再作處理。stream
其實看過並查集的實現就會發現,若是一個節點不是根節點,那麼跟這個節點有關的信息都是沒意義的。由於並查集只保證根節點的信息具備時效性,其餘非根節點的節點都只有一個做用,那就是找到根節點。nio
貼下代碼,這個模擬的過程仍是比較噁心人的。next
#include <cstdio> #include <iostream> using namespace std; const int maxn = 50050; int p[maxn], rank[maxn], prev[maxn], next[maxn]; void makeset(int x) { p[x] = x; rank[x] = 1; } int findset(int x) { int px = x, i; while(p[px] != px) px = p[px]; while(x != px) { i = p[x]; p[x] = px; x = i; } return px; } int unionset(int x, int y) { x = findset(x); y = findset(y); if(rank[x] > rank[y]) { p[y] = x; return x; } else { p[x] = y; if(rank[x] == rank[y]) rank[y]++; return y; } } int main() { // freopen("in.txt", "r", stdin); int N, K; cin >> N >> K; for(int i = 1; i <= N; i++) makeset(i); int ans = 0; for(int i = 0; i < K; i++) { int D, X, Y; scanf("%d%d%d", &D, &X, &Y); if(X > N || Y > N) { ans++; continue; } if(D == 1) { int x = findset(X), y = findset(Y); if(x == y) continue; if(y == prev[x] || y == next[x]) ans++; else { int a = prev[x], b = x, c = next[x]; int d = prev[y], e = y, f = next[y]; int o, p, q; if(a == 0) o = d; else if(d == 0) o = a; else o = unionset(a, d); p = unionset(b, e); if(c == 0) q = f; else if(f == 0) q = c; else q = unionset(c, f); prev[p] = o; next[p] = q; prev[o] = q; next[o] = p; prev[q] = p; next[q] = o; } } else { int y = findset(Y), x = findset(X); if(x == y) ans++; else { int a = x, b = next[x], c = prev[x]; int d = prev[y], e = y, f = next[y]; if(e == c || e == a) ans++; else { int o, p, q; if(b == 0) p = e; else p = unionset(b, e); if(d == 0) o = a; else o = unionset(a, d); if(c == 0) q = f; else if(f == 0) q = c; else q = unionset(c, f); prev[p] = o; next[p] = q; prev[o] = q; next[o] = p; prev[q] = p; next[q] = o; } } } } cout << ans << endl; return 0; }
記得對每一個元素保存捕食它的集合和它捕食的集合,集合固然都是根節點,並查集保證這一點。集合