poj1988(并查集)

转自:https://www.cnblogs.com/geloutingyu/p/6048326.html
题目链接:http://poj.org/problem?id=1988
题意:有n个箱子,初始时每个箱子单独为一列;

接下来有p行输入,M, x, y 或者 C, x;

对于M,x,y:表示将x箱子所在的一列箱子搬到y所在的一列箱子上;

对于C,x:表示求箱子x下面有多少个箱子;

用数组son存储当前节点的子树大小,用vis存储当前节点到根节点(最上面的箱子)的距离,所求值即son(find(x))-vis[x]-1;

按照一般并查集的合并方法,令pre[find(y)]=find(x);

那合并后会有: vis[find(y)]=son[find(x)]

       son[find(x)]+=son[find(y)]

代码:

#include <iostream>
#include <stdio.h>
#define MAXN 30001
using namespace std;

int pre[MAXN], son[MAXN], vis[MAXN];

int find(int x){
    if(pre[x]==x){
        return x;
    }
    int temp=pre[x];  //***递归思想,temp为存储x改变根节点后的根节点的临时变量
    pre[x]=find(pre[x]);
    vis[x]+=vis[temp];  //***x到改变前根节点的距离即x到temp的距离加上temp到根节点的距离
    return pre[x];
}

void jion(int x, int y){
    int px=find(x);
    int py=find(y);
    if(px!=py){
        pre[py]=px;
        vis[py]=son[px];  //***将x所在列放到y所在列上面后,find(y)到新合并后的根节点的距离即为合并前find(x)的子树的大小
        son[px]+=son[py];  //***合并后find(x)的子树大小即为合并前find(x)与find(y)的子树大小的和
    }
}

int main(void){
    int p;
    scanf("%d", &p);
    for(int i=1; i<=MAXN; i++){
        pre[i]=i;
        son[i]=1;
    }
    for(int i=1; i<=p; i++){
        char s[2];
        int x, y;
        scanf("%s", s);
        if(s[0]=='M'){
            scanf("%d%d", &x, &y);
            jion(x, y);
        }else{
            scanf("%d", &x);
            printf("%d\n", son[find(x)]-vis[x]-1);//***注意这里并不是输出son(x),因为我们并没有求出每个节点的子树的大小
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36300700/article/details/80076425