1182 食物鏈 並查集經典
阿新 • • 發佈:2019-01-25
思路:設r(x)表示節點x與根結點的關係,px表示x的根結點。記錄每個節點與其父節點的關係,就能很方便知道每個節點以及和它的父節點的關係。
struct node{
int par; //父親節點
int rel; //與父節點的關係
}a[maxn];
//關係:0表示同類, 1表示父節點吃子節點, 2表示子節點吃父節點
現在給定節點x和y,它們的關係是rel,如何判斷這句話是真還是假?
利用find函式找到它們的根結點px和py,以及它們和各自根結點的關係r(x)和r(y),如果px!=py說明x和y沒有位於同一集合,那麼這句話不會和任何話發生衝突,即這一定是真話,然後合併兩個集合;另一種情況就是px==py,說明x和y位於同一集合,現在已經得到x(x)和r(y),那麼如何知道x和y的關係呢?我先介紹一下find函式的實現:
int find(int x, int &r) {
if(a[x].par == x) {
r = x;
return a[x].rel;
}
int y = find(a[x].par, r);
a[x].par = r; //路徑壓縮
return a[x].rel = (a[x].rel + y) % 3;
}
find函式每次返回當前節點與根結點的關係,那麼在已知當前節點和它的父親節點關係,父親節點和根結點的關係,很容易得到當前節點與跟節點的關係r(x) = ( r(a[x].par) + a[x].rel) % 3。
同樣對於上面的問題,只需要變換一下x、root、y三者的關係,也能求得x和y的關係r(x, y),如果r(x, y) == rel,說明是真話,否則假話。
注意:所有的關係轉換都利用了find函式中的思想,請保證明確關係轉換才能看懂unionset函式。
AC程式碼
#include <cstdio> #include <cmath> #include <cctype> #include <algorithm> #include <cstring> #include <utility> #include <string> #include <iostream> #include <map> #include <set> #include <vector> #include <queue> #include <stack> using namespace std; #pragma comment(linker, "/STACK:1024000000,1024000000") #define eps 1e-10 #define inf 0x3f3f3f3f #define PI pair<int, int> typedef long long LL; const int maxn = 50000 + 5; struct node{ int par; //父親節點 int rel; //與父節點的關係 }a[maxn]; //關係:0表示同類, 1表示父節點吃子節點, 2表示子節點吃父節點 int find(int x, int &r) { if(a[x].par == x) { r = x; return a[x].rel; } int y = find(a[x].par, r); a[x].par = r; //路徑壓縮 return a[x].rel = (a[x].rel + y) % 3; } bool unionset(int x, int y, int rel) { int px, py; int rx = find(x, px), ry = find(y, py); if(px != py) { //位於同一集合 ry = (3 - ry) % 3; a[py].par = px; //合併 a[py].rel = (ry + rel + rx) % 3; return true; } else { rx = (3 - rx) % 3; if(rel == (rx + ry) % 3) return true; return false; } } int main() { int n, k; while(scanf("%d%d", &n, &k) == 2) { for(int i = 1; i <= n; ++i) { //初始化並查集 a[i].par = i; a[i].rel = 0; } int d, x, y, ans = 0; for(int i = 0; i < k; ++i) { scanf("%d%d%d", &d, &x, &y); if(x > n || y > n || (d == 2 && x == y)) { ans++; continue; } if(!unionset(x, y, d-1)) ans++; } printf("%d\n", ans); break; //只有一組資料 } return 0; }
如有不當之處歡迎指出!