题目链接: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 }