poj1988(并查集)
题目链接:http://poj.org/problem?id=1988
题意:有n个箱子,初始时每个箱子单独为一列;
接下来有p行输入,M, x, y 或者 C, x;
对于M,x,y:表示将x箱子所在的一列箱子搬到y所在的一列箱子上;
对于C,x:表示求箱子x下面有多少个箱子;
要注意题意是将x所在列的箱子叠到y所在箱子的上面,如果直接模拟的话就是将x最末端的叶子节点当做y的根节点的父亲节点合并,不过那样的话不好压缩路径,不压缩路径的话会超时.......
换个思路:
用数组son存储当前节点的子树大小,用vis存储当前节点到根节点的距离,所求值即son(find(x))-vis[x]-1;
按照一般并查集的合并方法,令pre[find(y)]=find(x);
那合并后会有: vis[find(y)]=son[find(x)]
son[find(x)]+=son[find(y)]
代码:
1 #include <iostream>
2 #include <stdio.h>
3 #define MAXN 30001
4 using namespace std;
5
6 int pre[MAXN], son[MAXN], vis[MAXN];
7
8 int find(int x){
9 if(pre[x]==x){
10 return x;
11 }
12 int temp=pre[x]; //***递归思想,temp为存储x改变根节点后的根节点的临时变量
13 pre[x]=find(pre[x]);
14 vis[x]+=vis[temp]; //***x到改变前根节点的距离即x到temp的距离加上temp到根节点的距离
15 return pre[x];
16 }
17
18 void jion(int x, int y){
19 int px=find(x);
20 int py=find(y);
21 if(px!=py){
22 pre[py]=px;
23 vis[py]=son[px]; //***将x所在列放到y所在列上面后,find(y)到新合并后的根节点的距离即为合并前find(x)的子树的大小
24 son[px]+=son[py]; //***合并后find(x)的子树大小即为合并前find(x)与find(y)的子树大小的和
25 }
26 }
27
28 int main(void){
29 int p;
30 scanf("%d", &p);
31 for(int i=1; i<=MAXN; i++){
32 pre[i]=i;
33 son[i]=1;
34 }
35 for(int i=1; i<=p; i++){
36 char s[2];
37 int x, y;
38 scanf("%s", s);
39 if(s[0]=='M'){
40 scanf("%d%d", &x, &y);
41 jion(x, y);
42 }else{
43 scanf("%d", &x);
44 printf("%d\n", son[find(x)]-vis[x]-1);//***注意这里并不是输出son(x),因为我们并没有求出每个节点的子树的大小
45 }
46 }
47 return 0;
48 }
我就是我,颜色不一样的烟火 --- geloutingyu