带权并查集的应用
题意: 说是有n块砖,编号从1到n,有两种操作,第一是把含有x编号的那一堆砖放到含有编号y的那一堆砖的上面
,第二是查询编号为x的砖的下面有多少块砖。
,第二是查询编号为x的砖的下面有多少块砖。
思路:带权并查集,用dis[i]表示元素i下边有多少个元素,num[i]表示元素i所在堆的砖块总数
或者说
dis[i]:当前元素到树根的距离
num[i]:当前元素所在树的大小(即树中元素个数)
或者说
dis[i]:当前元素到树根的距离
num[i]:当前元素所在树的大小(即树中元素个数)
Sample Input
6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4
Sample Output
1
0
2
#include <cstdio>
#define N 30005
using namespace std;
int dis[N], f[N], num[N];
void init()
{
for (int i = 1; i <= N; i++)
{
f[i] = i;
dis[i] = 0;//每个元素初始状态,下边没有砖块
num[i] = 1;//每堆初始只有1个砖块
}
}
int find(int x)
{
if (x == f[x])
return x;
else
{
int tmp = f[x];
f[x] = find(tmp);
dis[x] += dis[tmp];
//压缩路径后,当前元素的cnt值==他原来父亲(即路径压缩之前的父亲)的cnt值+当前元素相对于原来父亲的cnt值
//也就是说,当它的父亲完成路径压缩时,它的cnt值将是它与原来父亲的相对值加上现在父亲与“顶头上司”的相对值
return f[x];
}
}
void merge(int x, int y)
{
int t1 = find(x), t2 = find(y);
if (t1 != t2)
{
f[t1] = t2;
dis[t1] = num[t2];//记录当前元素下边的砖块数。
num[t2] += num[t1];//记录集合元素总数
}
}
int main()
{
int T;
while (scanf("%d", &T) != EOF)
{
init();
while (T--)
{
getchar();
int u, v;
char ch;
scanf("%c", &ch);
if (ch == 'M')
{
scanf("%d%d", &u, &v);
merge(u, v);
}
else
{
scanf("%d", &u);
find(u);//每次查询时,再次路径压缩,更新dis
printf("%d\n", dis[u]);
}
}
}
return 0;
}