题目背景
\(Cube Stacking, 2004 Open\)
题目描述
约翰和贝西在叠积木。共有\(30000\)块积木,编号为\(1\)到\(30000\)。一开始,这些积木放在地上,自然地分成\(N\)堆。贝西接受约翰的指示,把一些积木叠在另一些积木的上面。一旦两块积木相叠, 彼此就再也不会分开了,所以最后叠在一起的积木会越来越高。约翰让贝西依次执行\(P\)条操作,操作分为两种:
第一种是移动操作,格式为“移动\(X\)到\(Y\)的上面”。\(X\)和\(Y\)代表两块积木的编号,意思是将X所的那堆积木,整体叠放到\(Y\)所在的那堆积木之上;
第二种是统计操作,格式为“统计\(Z\)下方的积木数量”。\(Z\)代表一块积木的编号,意思是贝西需要报告在编号为\(Z\)的积木之下还有多少块积木
请编写一个程序,帮助贝西回答每条统计问题。
输入输出格式
输入格式:
第一行:单个整数:\(P,1 ≤ P ≤ 10^5\)
第二行到第\(P + 1\)行:每行描述一条命令,如果这行开头的字母是 \(M\),代表一条移动命令,后面的两个整数代表上文中的X和\(Y\);如果开头字母是 \(C\),代表一条统计命令。后面的整数代表上文中的\(Z\),保证所有的移动命令都有意义,\(X\)和\(Y\)不会已经出现在同一堆积木里
输出格式:
对每一个统计命令,输出正确回答,用换行符分开每个查询的结果
输入输出样例
输入样例#1:
6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4
输出样例#1:
1
0
2
说明
第一次查询时, \(1\) 下面只有一个 \(6\);第二次查询时, \(3\) 下面没有任何积木;第三次查询时,\(4\) 下面有两块积木:\(1\) 和 \(6\)
思路:并查集的题目就是不一样,代码短小精悍,思路却很难想……对于这道题目来说,我们用一个\(s\)数组表示某个积木上方有多少个积木,再用一个数组\(a\)表示以某个点为顶的积木的个数,在并查集路径压缩的过程中让\(s[x]+=s[fa[x]]\),就是说\(x\)的父亲是在\(x\)上面的,那么就把它父亲上面的积木也加到\(x\)上面来,然后对于每个移动命令,是把以\(x\)为顶的那一块放到\(y\)上面,那么我们就更新\(x\)是\(y\)的父亲,然后以\(x\)为顶的积木个数变成了原本以\(x\)为顶的积木个数加上原本以\(y\)为顶的积木个数,\(y\)上方的积木个数就加上了原本以\(x\)为顶的积木个数,然后对于每个统计命令,因为要求对于\(x\)输出\(x\)下面的积木的个数,那么就是以\(x\)为顶的积木个数减去\(x\)上面的积木个数再减\(1\)(因为\(x\)不在\(x\)的下方)。
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#define maxn 30007
using namespace std;
int p,fa[maxn],s[maxn],a[maxn];
string s1;
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
int find(int x) {
if(fa[x]==x) return x;
int res=find(fa[x]);
s[x]+=s[fa[x]];
return fa[x]=res;
}
int main() {
p=qread();
for(int i=1;i<=30000;++i) a[i]=1,fa[i]=i;
for(int i=1,x,y;i<=p;++i) {
cin>>s1;
if(s1=="M") {
x=qread(),y=qread();
int zrj=find(x),cyh=find(y);
if(zrj!=cyh) {
fa[cyh]=zrj;
s[cyh]+=a[zrj];
a[zrj]+=a[cyh];
a[cyh]=0;
}
}
else {
x=qread();
int zrj=find(x);
printf("%d\n",a[zrj]-s[x]-1);
}
}
return 0;
}