题目:

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;
}