初涉「带权并查集」&&bzoj3376: [Usaco2004 Open]Cube Stacking 方块游戏

算是挺基础的东西

Description

    约翰和贝茜在玩一个方块游戏.编号为1到n的n(1≤n≤30000)个方块正放在地上.每个构成一个立方柱.
   游戏开始后,约翰会给贝茜发出P(1≤P≤100000)个指令.指令有两种:
    1.移动(M):将包含X的立方柱移动到包含Y的立方柱上.
    2.统计(C):统计名含X的立方柱中,在X下方的方块数目.
    写个程序帮贝茜完成游戏.

Input

    第1行输入P,之后P行每行输入一条指令.形式为“M X Y”或者“C X”
    输入保证不会有将立方柱放在自己头上的指令.

Output

    每一行,对于每个统计指令,输出其结果.

Sample Input

6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4

Sample Output

1
0
2

题目分析

带权并查集的模板题。

之所以能够用并查集做,是因为:即便集合内每一个元素不是完全等价的,但它满足能够独立带权的性质。

嗯……如果是初学带权并查集的话还是无视上面那句话好了。学的时候还是靠代码理解,理解之后自然就会有自己对于一块方面的总结了。

对于每一个维护$dis[i]$和$sum[i]$,其中$dis[i]$表示它下面有几个方块;$sum[i]$表示它这摞方块一共有几个。维护两个量是为了方便合并时的转移。

因为这题有上下之分,所以合并过程是既不能随便合并(破坏顺序);也难以按秩合并(麻烦)。路径压缩看上去好像也不行,然而实际上可以这样操作:保留逻辑上的上下顺序(换句话说,就是我们在考虑问题时候当做它是上下有序),但是操作时就路径压缩(运行结果上来看,元素之间路径被压缩了,因此并没有上下顺序)。

这样便发现,合并时候把整摞方块的最下面方块作为根是更方便的。

 1 #include<cstdio>
 2 #include<cctype>
 3 const int maxn =  30035;
 4 
 5 int fa[maxn],sum[maxn],dis[maxn];
 6 int q;
 7 
 8 int get(int x)
 9 {
10     if (fa[x]!=x){
11         int tt = fa[x];
12         fa[x] = get(fa[x]);
13         dis[x] += dis[tt];
14     }
15     return fa[x];
16 }
17 void unions(int x, int y)
18 {
19     fa[x] = y;
20     dis[x] += sum[y];
21     sum[y] += sum[x];
22 }
23 int main()
24 {
25     scanf("%d",&q);
26     for (int i=1; i<=30000; i++) fa[i] = i, sum[i] = 1;
27     for (int i=1; i<=q; i++)
28     {
29         char ch = getchar();
30         while (!isalpha(ch)) ch = getchar();
31         int x,y;
32         if (ch=='M'){
33             scanf("%d%d",&x,&y);
34             int fx = get(x), fy = get(y);
35             if (fx!=fy) unions(fx, fy);
36         }else{
37             scanf("%d",&x);
38             get(x);
39             printf("%d
",dis[x]);
40         }
41     }
42     return 0;
43 }

END

原文地址:https://www.cnblogs.com/antiquality/p/9316386.html