题意是给出N个立方体,可以将立方体移动到其它立方体形成堆,然后有P个下面的操作: 1) M X Y ,将X立方体所在的堆移到Y立方体所在的堆的上面; 2) C X 输出在X所在的堆上,在X立方体下面的立方体个数。
使用并查集来解决这个问题。关键在于怎么存储和更新立方体的结果(即操作2的输出值)。用三个数组,p,h,t, p[i]表示i的根结点,h[i]表示i的结果,即压在i下面的立方体个数,t[i]表示i所在的堆的立方体总个数。对于每一堆立方体,根结点使用堆底的立方体,而且在这个堆所对应的集合内,通过更新,使得只有根结点的t值为这堆的总个数,h值为0(因为它在堆底),其它的立方体的t值都为0,h值在并查集的查找步骤中进行递归更新。
在并查集的查找函数的执行中,先向上找到根结点,并且保存当前结点x的父节点为temp,找到根结点后,向下依次一更新结点的h,t值。
1)若t[x]不为0,即表示x是一个堆的堆底元素,h[x]为0,其父节点是另外一堆的堆底(因为在并查集的操作中,通过将一个堆的堆底指向另一个堆的堆底来实现合并), h[x]+=t[temp],t[temp]+=t[x],t[x]=0 ,这三个语句将x的h值加上父结点的总个数(因为是将x所在的堆放在父节点的堆),然后将父节点的t值加上x的t值(父节点的堆的总数变为两者之和),然后再将x的t值置0.
2)若t[x]为0,即表示x不是堆底,那么只要将x的h值加上父节点的h值即可。h[x]+=h[temp] 。
画个图然后稍微分析查找操作的过程就能得到上面的结果。下面是并查集的几个函数。在合并操作里面,合并完后我们再对x,y执行一次查找操作以更新对应堆的值,因为在下次合并的时候可能堆还没有来得及更新。
1 void make_set()
2 {
3 int i;
4 for(i=1;i<N;++i)
5 {
6 p[i]=i;
7 h[i]=0;
8 t[i]=1;
9 }
10 }
11 int find_set(int x)
12 {
13 int temp;
14 if(x!=p[x])
15 {
16 temp=p[x];
17 p[x]=find_set(p[x]);
18 if(t[x]!=0)
19 {
20 h[x]+=t[temp];
21 t[temp]+=t[x];
22 t[x]=0;
23 }else
24 {
25 h[x]+=h[temp];
26 }
27 }
28 return p[x];
29 }
30 void union_set(int x,int y)
31 {
32 int px=find_set(x),py=find_set(y);
33 p[px]=py;
34 find_set(x),find_set(y);
35 }
36