题目链接:http://poj.org/problem?id=1988
题意:有n个方块,编号为1-n,现在存在两种操作:
- M i j 将编号为i的方块所在的那一堆方块移到编号为j的方块所在的那一堆方块上方,方块原先的相对位置保持不变
- C i 计算在方块i下面有多少个方块
虽然知道这道题用并查集做,但并没什么思路。看了看题解:用h[i]维护当前方块到其父亲方块的距离,num[i]维护方块i所在的堆的方块数,每次合并(unite)时,将最下面的方块作为根节点,并更新新堆的木块数。每次查询路径压缩时,更新节点父亲的同时,也更新该节点到父亲(此时父亲即是根)的距离h[i],因此每次计算木块数目时,需要执行一次查询操作,将h[i]更新为到根的距离再输出
#include <iostream> #include <algorithm> #include <cstdio> using namespace std; class union_find_set { public: union_find_set(int n) { //并查集初始化 fa = new int[n]; h = new int[n]; num = new int[n]; for (int i = 0; i < n; i++) { fa[i] = i; num[i] = 1; h[i] = 0; } } ~union_find_set() { delete fa,h,num; }; int find(int x) { //查询函数 if (fa[x] == x) return x; int t = fa[x]; fa[x] = find(fa[x]); h[x] += h[t]; return fa[x]; } void unite(int x, int y) { //合并函数 x = find(x); y = find(y); if (x != y) { fa[x] = y; h[x] = num[y]; num[y] += num[x]; } } bool same(int x, int y) { //判断是否在一个堆 if (find(x) == find(y)) return 1; return 0; } int n; int *fa,*h,*num; }; int main() { int n; scanf("%d", &n); getchar(); //cout << n << endl; union_find_set cube(n + 1); for (int i = 1;i <= n;i++) { char c; scanf("%c", &c); //cout << c << -1 << endl; int a, b; if (c == 'M') { scanf("%d%d", &a, &b); cube.unite(a, b); } else { scanf("%d", &a); cube.find(a); printf("%d\n", cube.h[a]); } getchar(); } }