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