hdu2818Building Block

题目大意:

有一堆箱子,编号是1~N,现在对这堆箱子进行如下操作;

M X Y;将X放到Y上去,并与Y变成一个整体,即下次执行M X Z;的时候,是将X和Y按原来的顺序搬到Z的上方。

C X;问X下面有多少箱子。

解题思路:

这道题其实非常有意思,当时自己没做出来,找了很多代码后才发现一个比较直观的思路。

       这个思路是用并查集做的,但是得用一点逆向思维,它要求我们求出下方有多少箱子,但是求下方的箱子是比较难的!

为什么说记录下方的箱子数目比较难呢?,我们看一个例子。


    假设我们要将集合1放到集合三上面的话,我们在find(4)和find(5)的时候顺便维护一个under[]数组,记录每个元素的下面的元素的数量。这一步是很容易实现的,合并也就如上图中的2,4所示。然后我们要将3,4合并到5这一步来,这个时候我们每个节点都有一个under[i]记录他下面的子节点数,但是合并之后下面的under[]肯定是不受影响,但是上方的under[]就肯定会受影响了,而且每个under[]都得加上下面那个集合里的元素的总个数,问题就出在这了,这一步就非常的难实现,为什么呢?我们知道,2,4都是被压缩了,因此我们根据子节点找父节点容易,但是要根据父节点找子节点却比较难,而且我们路径压缩的时候也只是将下面的跟新,但是恰巧下面的有不必更新。所以问题的关键就是每次都更新下边的而不更新上边的。于是将问题转换一下,用up数组记录上面的元素的个数,然后拿这个集合的总格数SetSize-up-1就能求出下边的了!

#include<stdio.h>
#define N 30009
int parent[N],SetSize[N],up[N];
void initi(){
  int i;
  for(i=0;i<N;i++){
    parent[i]=i;
    SetSize[i]=1;
    up[i]=0;
   }
}
int getParent(int x){
     int temp;//路径压缩并更新子节点up
     if(x!=parent[x]){
         temp=parent[x];
         parent[x]=getParent(parent[x]);
         up[x]+=up[temp];
     }
     return parent[x];
}
void Union(int a,int b){
    a=getParent(a);
    b=getParent(b);
    if(a==b)
        return ;
    parent[b]=a;
    up[b]=SetSize[a];
    SetSize[a]+=SetSize[b];
}
int main()
{
    int n,a,b;
    char opera[3];
    while(scanf("%d",&n)!=EOF){
            initi();
            while(n--){
            scanf("%s",opera);
            if(opera[0]=='M'){
                    scanf("%d%d",&a,&b);
                    Union(a,b);
            }else{
                scanf("%d",&a);
                b=getParent(a);
                printf("%d\n",SetSize[b]-up[a]-1);

            }
        }

    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值