题目:
http://poj.org/problem?id=1988
题意:
有n个箱子,标号为1~n,初始时分成n堆,每堆一个按顺序。现在有p个操作,操作分两种:
- M x y:把x所在的那一堆放到y所在的那一堆上
- C x:查询x下面有多少个箱子
poj和hdu不同的一点是:hdu可能出现x y在同一堆的情况
思路:
定义rnk[x]为x到父节点的距离,num[x]为以x为祖先的所有点的个数。执行M操作时,首先找出x、y的祖先fx、fy,然后把fy作为fx的祖先,可以发现原本应该在x下面的箱子,在这样的表示下被扔到了x的上面,也就是倒着来的,这样执行查询操作时,x到祖先节点的距离就是答案。我们再来看fx和fy合并时距离怎么确定,已知fy是父节点,那么fx到fy的距离就应该是num[fy],仔细想一下便知,然后在更新num[fy],因为合并了fx及其子孙后点数就变了,当时把fx到fy的距离这个地方算错了,套用了带权并查集计算种类之间关系的式子,错的离谱。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
const int N = 30000 + 10, INF = 0x3f3f3f3f;
int par[N], rnk[N], num[N];
void init()
{
for(int i = 0; i < N; i++) par[i] = i, num[i] = 1, rnk[i] = 0;
}
int ser(int x)
{
if(x != par[x])
{
int fx = ser(par[x]);
rnk[x] = rnk[x] + rnk[par[x]];
par[x] = fx;
}
return par[x];
}
void unite(int x, int y, int type)
{
int fx = ser(x), fy = ser(y);
if(fx == fy) return;
rnk[fy] = num[fx];
num[fx] += num[fy];
par[fy] = fx;
}
int main()
{
int n, x, y;
char ch;
while(~ scanf("%d", &n))
{
init();
for(int i = 1; i <= n; i++)
{
scanf(" %c", &ch);
if(ch == 'M')
{
scanf("%d%d", &x, &y);
unite(y, x, 1);
}
else
{
scanf("%d", &x);
ser(x);
printf("%d\n", rnk[x]);
}
}
}
return 0;
}