POJ 1988 Cube Stacking(路径压缩并查集)

POJ 1988 Cube Stacking(路径压缩并查集)

http://poj.org/problem?id=1988

题意:

        有N(1<=N<=30000)个相同的木块(编号1到N),初始时每个模块做一堆放。接着要执行下面P条命令:

        M a b 表示把含有积木a的那堆木块整体放到含有积木b的那堆木块上。

        C a 要求输出含有a的那堆木块中,a木块下面一共有多少木块。

分析:

        主要就是并查集路径压缩,如果M a b就等于把a所属的连通分量连接到b所属的连通分量上。

        但是并查集的每个节点维护3个变量:父节点编号fa[i],其与父节点之间有多少个节点(不包括当前节点但包括父节点)v[i],以当前节点为根的连通分量一共有多少节点total[i]。

        由本节点离父节点直接还有多少个节点(初始为0,)可以回答C询问,即用当前分量的总节点数 减去 当前节点到根节点之间(包括根)的节点数即可。

        当进程M操作时候比如M a b 那么 a属于的连通分量合并到b所属的连通分量上.a的连通分量的根节点需要更新其v[fa]=total[fb](这步先更新,想想为什么这么更新),b所属的连通分量需要更新的是total[fb]+=total[fa](这步后更新).

AC代码:297ms

#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=30000+100;
int F[MAXN];//F[i]表示i节点的父节点
int v[MAXN];//v[i]表从i到其父节点有多少个节点(包括父节点)
int total[MAXN];//total[i]表若i节点为根,那么i节点所属的连通分量有多少个节点
int findset(int i)
{
    if(F[i]==-1)return i;
    int temp = findset(F[i]);
    v[i] += v[F[i]];
    return F[i]=temp;
}
void bind(int i,int j)//将i的连通分量合并到j的连通分量上
{
    int fa=findset(i);
    int fb=findset(j);
    if(fa!=fb)
    {
        F[fa]=fb;
        v[fa]=total[fb];
        total[fb]+=total[fa];
    }
}
int main()
{
    int p;
    while(scanf("%d",&p)==1&&p)
    {
        memset(F,-1,sizeof(F));
        memset(v,0,sizeof(v));
        for(int i=0;i<MAXN;i++)
            total[i]=1;
        while(p--)
        {
            char str[10];
            int a,b;
            scanf("%s",str);
            if(str[0]=='M')
            {
                scanf("%d%d",&a,&b);
                bind(a,b);
            }
            else if(str[0]=='C')
            {
                scanf("%d",&a);
                findset(a);
                printf("%d\n",v[a]);
            }
        }
    }
}

你可能感兴趣的:(ACM)