叠积木/银河系英雄传说[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;
}
posted @ 2019-01-04 14:08  __allenge  阅读(147)  评论(0编辑  收藏  举报