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