叠积木/银河系英雄传说[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
",under[x]);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/GDOI2018/p/10219537.html