叠积木/银河系英雄传说[NOI2002]题解
题目右转 luogu P2342
其实这道题和 [NOI2002]银河英雄传说
一模一样(双倍经验)
言归正传:
题目中的 “移动 \(X\) 到 \(Y\) 的上面” 操作可以看成是在并查集中将 \(X\) 和 \(Y\) 合并入同一个集合
而对于 “统计 \(Z\) 下方的积木数量” 操作,我们需要维护两个数组:\(high[X]\) 表示第 \(X\) 中积木的数量, \(under[X]\) 表示积木 \(X\) 下方的积木数量
为什么要维护 \(high[X]\) 呢 \(?\) 原因是并查集一旦经过路径压缩之后,所有节点都会连到根节点(也就是说,当两个集合合并后,同时都连到了根节点,成为了并列的节点,不存在谁在谁上面了)
\(high\) 数组可以在和并的时候维护,如:将以 \(r1\) 为根的积木移动到以 \(r2\) 为根的积木上方
\(high[r2]+=high[r1],high[r1]=0;\)
\(under\) 数组就麻烦一点了,不仅在合并的时候需要维护:
\(under[r1]=high[r2];\)
在路径压缩的时候也需要维护一下:
int get(int x)
{
if(f[x]==x) return x;
int temp=f[x]; //记录父节点
f[x]=get(f[x]); //路径压缩
under[x]+=under[temp]; //递归维护一下
return f[x];
}
就到这里了,接下来贴代码 有点小激动
#include<cstdio>
using namespace std;
const int maxn=30001;
int f[maxn],under[maxn],high[maxn],n;
int get(int x)
{
if(f[x]==x) return x;
int temp=f[x];
f[x]=get(f[x]);
under[x]+=under[temp];
return f[x];
}
void U(int x,int y)
{
int r1=get(x),r2=get(y);
if(r1==r2) return ;
f[r1]=f[r2];
under[r1]=high[r2],high[r2]+=high[r1],high[r1]=0;
return ;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<maxn;i++)
f[i]=i,high[i]=1;
for(int i=1;i<=n;i++)
{
char a[2];
scanf("%s",&a);
if(a[0]=='M')
{
int x,y;
scanf("%d%d",&x,&y);
U(x,y);
}
else
{
int x;
scanf("%d",&x);
int t=get(x);
printf("%d\n",under[x]);
}
}
return 0;
}