该题是个比较明显的并查集题目,只不过需要求的不是一个集合中元素的个数,而是某个数下面的积木个数。
显然,我们需要维护一些量,来达到计算的目的。 为了使得并查集高效,我们显然不能放弃了路径压缩,那样会导致并查集退化成一条长长的链,效率十分低下。
但是我们可以注意到,由于路径压缩的缘故,每次遍历到的点都会被直接连到祖先结点上。那么我们可以维护一个under数组,以under[i]表示结点i之下有多少积木。
但是这样会有一个问题,在合并操作之后怎么搬? 我们可以发现,对于每一堆积木,个数只会增加不会减少,所以under[i]也只会增加不可能减少,所以我们不妨再维护每个集合里的元素个数,将这个量维护在根结点上,每次合并的时候只需要更新移动的那堆积木根结点i的under[i],以及集合元素个数。
该题需要对并查集的原理非常熟悉,望读者好好体会。
细节参见代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int INF = 1000000000;
const int maxn = 30000 + 5;
int n,q,m,a,b,p[maxn],cnt[maxn],under[maxn];
void init() {
for(int i=0;i<maxn;i++) {
p[i] = i;
cnt[i] = 1;
under[i] = 0;
}
}
int setfind(int x) {
if(p[x] == x) return x;
else {
int root = setfind(p[x]);
under[x] += under[p[x]];
return p[x] = root;
}
}
char s[5];
int main() {
while(~scanf("%d",&q)) {
init();
while(q--) {
scanf("%s",s);
if(s[0] == 'M') {
scanf("%d%d",&a,&b);
int x = setfind(a), y = setfind(b);
if(x != y) {
p[x] = y;
under[x] = cnt[y];
cnt[y] = cnt[x] = cnt[x]+cnt[y];
}
}
else {
scanf("%d",&a);
int x = setfind(a);
printf("%d\n",under[a]);
}
}
}
return 0;
}