题目链接
http://poj.org/problem?id=1182
思路
前面做的带权并查集的权值记录该结点与其父结点是否是同一类,只有两种取值情况(0,1),在这题中某结点a和其父结点b的取值共有三种情况:a和b同类;a被b吃;a吃b,对应三种情况,r[a]的取值分别为0,1,2。这题的难点是递推公式,路径压缩递推公式为r[x] = (r[x] + r[t]) % 3,合并集合递推公式为r[ra] = (3 - r[a] + d - 1 + r[b]) % 3,递推公式形式的推导可以参考这篇文章。
代码
1 #include <cstdio> 2 3 const int N = 500000 + 10; 4 int p[N]; 5 int r[N]; 6 7 void make_set(int n) 8 { 9 for (int i = 1;i <= n;i++) 10 { 11 p[i] = -1; 12 r[i] = 0; 13 } 14 } 15 16 int find_root(int x) 17 { 18 if (p[x] == -1) 19 return x; 20 21 int t = p[x]; 22 p[x] = find_root(p[x]); 23 r[x] = (r[x] + r[t]) % 3; //路径压缩递推公式 24 return p[x]; 25 } 26 27 int union_set(int d, int a, int b) 28 { 29 int ra = find_root(a); 30 int rb = find_root(b); 31 32 if (ra != rb) //a,b不在同一集合,合并 33 { 34 p[ra] = rb; 35 r[ra] = (3 - r[a] + d - 1 + r[b]) % 3; //合并集合递推公式 36 return 0; 37 } 38 else 39 { 40 if ((r[a] + 3 - r[b]) % 3 != d - 1) 41 return 1; 42 else return 0; 43 } 44 return 0; 45 } 46 47 int main() 48 { 49 //freopen("poj1182.txt", "r", stdin); 50 int n, k; 51 scanf("%d%d", &n, &k); 52 make_set(n); 53 int d, x, y; 54 int ans = 0; 55 for (int i = 0; i < k;i++) 56 { 57 scanf("%d%d%d", &d, &x, &y); 58 if (x > n || y > n || (d == 2 && x == y)) 59 ans++; 60 else ans+=union_set(d, x, y); 61 } 62 printf("%d\n", ans); 63 return 0; 64 }
参考:
1、http://www.voidcn.com/article/p-mwvpmony-qx.html
2、https://www.cnblogs.com/zhuanzhuruyi/p/5863738.html
时间: 2024-10-03 15:10:14