POJ1988 Cube Stacking 【并查集】

题目链接:http://poj.org/problem?id=1988

这题是教练在ACM算法课上讲的一道题,当时有地方没想明白,现在彻底弄懂了。

题目大意:n代表有n个石头,M a, b代表将a石头所在的集合放在b石头所在的所有集合的上方。C a代表询问a石头下方有多少个石头。

思路:

1.一开始我是想用一个 num 数组来表示每块石头下方有多少个石头,然后直接用并查集来写,但是发现最终不能实现,因为只用一个num数组只能保证每个集合的最顶端的石头num值是正确的。

2.正解是用一个 dis 数组代表每块石头到根节点石头的距离,num数组代表每块石头所在集合的石头总数,最终答案就是 num[find(a)] - dis[a] - 1;

3.需要注意的地方是 先查找根节点 后更新dis以及num,因此在查询某一块石头下面有多少石头时,还需要进行一次 find 来更新一下路径上石头的dis。

代码里的注释写的很详细

代码:

 注意:数据好像比题面给的大,数组要开大点。

 1 #include<iostream>  //用cin输入无视空格和回车,方便判断操作 
 2 using namespace std;
 3 
 4 int num[300010], pre[300010], dis[300010];
 5 
 6 int find(int x)
 7 {
 8     if(pre[x] == x)
 9         return x;
10     else
11     {
12         int root = find(pre[x]);
13         dis[x] += dis[pre[x]];//下面的块中点到新根节点的距离为 原来的距离dis[x]加上被更新过的dis[root] = num[x]的距离 
14         pre[x] = root;
15         return pre[x];
16     }
17 }
18 
19 int main()
20 {
21     cin.sync_with_stdio(false);//关闭cin同步加速,但读入只能用cin了 
22     int n;
23     cin >> n;
24     for(int i = 1; i <= n; i ++)
25     {
26         pre[i] = i;
27         dis[i] = 0;//到根节点的距离 
28         num[i] = 1;//每个块中包含的点数目 
29     }
30     while(n --)
31     {
32         char ch;
33         cin >> ch;
34         if(ch == 'M')
35         {
36             int a, b;
37             cin >> a >> b;
38             int x = find(a), y = find(b);//此时的find只是找到根节点,find()函数中各点dis值没有改变,因为此时根节点的dis为0 
39             if(x != y) 
40             {//注意顺序 
41                 dis[y] = num[x];//这里才给根节点的dis值进行了更新,所以下次还需要调用一次find才能维护各点的dis值 
42                 num[x] += num[y];//以x为根节点的块中数目更新为 以x和y为根节点的两个块中点的数目之和 
43                 pre[y] = x;
44             }
45         }
46         else if(ch == 'C')
47         {
48             int a;
49             cin >> a;
50             int x = find(a);//查询之前还需要进行一次 find 来更新块中各点的dis值 
51             printf("%d
", num[x] - dis[a] - 1);
52         }
53     }
54     return 0;
55 }
POJ1988
原文地址:https://www.cnblogs.com/yuanweidao/p/10928308.html