题目链接: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();
}
}