题目:http://acm.hdu.edu.cn/showproblem.php?pid=2473
题意:有0到n-1个数,M a b 表示a和b合并,S a表示删除a,删除以后a变成独立的集合。。。最后问共有几个集合。。。。
开始做的时候是真的按步就搬地把a删除,然后把他儿子和他老爸都合起来。。。结果超时。。。。
还有吧本来是int 型的a,b写成了 char型导致一直溢出,找了很久才找到。。。
这里用了代理index[i]表示i的代理是index[i],然后以后操作的时候每个人干活只找代理干,然后删除的时候直接换个新代理(这个代理不能和别人的代理冲突,开始的时候是独立的集合),
然后他原来的代理事实上还在本来那里,没有删除,因为我们只找代理来干活,而他原来的代理已经不是代理了,所以不会找到原来那个代理。。。。。
最后统计的时候,正常来说我们可以扫一遍代理,看father[代理] == 代理就累加,而这里不正常,会计少了集合的。。。。。
因为当你删除的代理是根,而该树的儿子孙子还有别人的代理,即该集合也算一个集合,当你最后找的时候,因为只找代理,而那些算一个集合的,而根本来是代理,因为删除后而不是代理了的集合就会没有统计。。。。
所以我们扫所有代理用Find()来找根,然后用标记法(因为可能多个代理同一个根)来把根标记,最后统计根的个数
#include <iostream> using namespace std; const int M = 10000100; int father[M]; int rank1[M]; int save[M]; int index[M]; bool used[M]; void Make_Set(int x) { father[x] = x; rank1[x] = 0; } int Find(int x) { int k = 0; while (x != father[x]) { save[k++] = x; x = father[x]; } for (int j = 0; j < k; j++) { father[save[j]] = x; } return x; } void Union(int a, int b) { int x = Find(a); int y = Find(b); if (x == y) { return; } if (rank1[x] < rank1[y]) { father[x] = y; } else { father[y] = x; if (rank1[x] == rank1[y]) { rank1[x]++; } } } int main() { int n, m; int case1 = 0; while (~scanf("%d%d", &n, &m), n + m) { for (int i = 0; i < n; i++) { Make_Set(i); index[i] = i;//开始代理都是本身 } int k = n+1; while (m--) { char str[10]; int a, b; scanf("%s", str); if (str[0] == 'M') { scanf("%d%d", &a, &b); Union(index[a], index[b]); } else { scanf("%d", &a); index[a] = k++;//换代理 Make_Set(index[a]); } } int count = 0; memset(used, 0, sizeof(used)); for (int i = 0; i < n; i++) { //if (father[i] == i)或father[index[i]] == index[i] 都是错的 //{ // count++; //} used[Find(index[i])] = 1;//标记根 } for (int i = 0; i < k; i++) { if (used[i]) { count++; } } printf("Case #%d: %d\n", ++case1, count); } return 0; }