【HDU 2818】 Building Block 【带权并查集】

John are playing with blocks. There are N blocks (1 <= N <= 30000) numbered 1…N。Initially, there are N piles, and each pile contains one block. Then John do some operations P times (1 <= P <= 1000000). There are two kinds of operation:

M X Y : Put the whole pile containing block X up to the pile containing Y. If X and Y are in the same pile, just ignore this command.
C X : Count the number of blocks under block X

You are request to find out the output for each C operation.
Input
The first line contains integer P. Then P lines follow, each of which contain an operation describe above.
Output
Output the count for each C operations in one line.
Sample Input
6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4
Sample Output
1
0*
2

分析:想了许久,终于a了。
建议本题 自己仔细想想 再看题解,自己想通之后 你会对带权并查集有个更加清晰的理解。
对于基础的并查集 你需要知道 :用pre[]和Rank[] 可以很好的动态维护任何一个点属于哪个集合,和每个集合有几个元素。
刚想本题的时候,你会发现有两个值不好维护 。
第一个 : 对于M 操作,你如何快速的判断x和y分别在哪个盘子上? 只是这一个问题的话,很好解决,用一个并查集就足够。
第二个问题: 对于C操作,你如何快速的判断其下方有几个物体。我们可以试着用上一个问题的思路来想 ,现在有一个并查集来维护,我们可以快速的知道C是属于哪个盘子的,但是其下方的值呢? 。 这里是最不好想的一个地方,这个问题的话,你需要自己多模拟几遍这个游戏的过程,看懂这个M操作会对C操作有什么影响。 很快你就会发现,对于M x y (x的盘子tx,y的盘子ty)会导致 tx 上的所有物品 其下方所放物品的数目都会加上 ty盘子上所有物品的数目。看懂这一点之后 就是带权并查集的用处了,relation[i] 代表i 和pre[i]之间有几个物品。然后我们 路径压缩的时候,就可以得到每个物品对于 集合代表元素的 物品差 。
代码

#include<cstring>
#include<iostream>
#include<queue>
#include<cstdio>
using namespace std;
#define LL long long

const int N = 30000+11 ;
const int M = 1e6;
const int mod = 7653;
const int inf = 0x3f3f3f3f;

int pre[N],relation[N],Rank[N];
void init(){
    for(int i=0;i<N;i++){
        pre[i]=i;
        relation[i]=0;
        Rank[i]=1;
    }
}

int Find(int x){
    if(x==pre[x]) return x;
    int t=pre[x];
    pre[x]=Find(pre[x]);
    relation[x]= relation[x]+relation[t]; // 路径压缩 
    return pre[x];
}
void Join(int x,int y){
    int tx=Find(x); int ty=Find(y);
    if(tx!=ty){
        relation[tx]+=Rank[ty];
        Rank[ty]+=Rank[tx]; pre[tx]=ty;
    }
}
int main(){
    int n,m;
    while(scanf("%d",&m)!=EOF){
        init();
        while(m--){
            char s[5];scanf("%s",s);
            if(s[0]=='M'){
                int a,b;scanf("%d%d",&a,&b);
                Join(a,b);
            }else if(s[0]=='C'){
                int x; scanf("%d",&x);
                 Find(x); // 这里注意,一定要路径压缩一下 
                printf("%d\n",relation[x]);
            }
        }
    }
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值