HDU 2818 Building Block (带权并查集)

来源:互联网 发布:有深度的书籍推荐知乎 编辑:程序博客网 时间:2024/06/06 14:23

参考http://blog.163.com/i_am_an_alp/blog/static/2403891682014921102227653/

这个题感觉很有意思!!!!!!!!
还是花了些时间的
题意:给你p种操作,操作分别有
1 x y 表示将带有x的一堆放到y的上面
2 x 表示询问x的下面有多少个blocks
对于每次的询问,我们都要输出相应的答案

分析:我们怎么知道一个数下面有多少个数呢?把一堆放到另一堆上,我们只能知道上一堆的最下面一个的答案是下面那一堆的节点数,那上面的那一堆其他的答案怎么求呢,相当于他们原来本身的加上 下面一堆的。
所以我们开了三个数组
f:表示跟的关系
r[i]:表示以i为根的节点的个数
under[i]:表示i下面有多少个节点

然后这个题我感觉最有趣的地方是under数组的运用,我们之前都用过f和r数组,记录根的关系以及节点数。
但是这道题我们要求一个数下面有几个数,这个时候我们用并查集的时候要有一个顺序了,不然就不知道谁是最后一个了,所以我们要用最后一个节点作为根。然后怎么求i下面有多少个节点呢?我们每次将x堆放到y堆上面的时候,我们可以知道x底下(假设为z点)z点底下有多少个,就是y的节点数,然后z上面的点通过路径压缩也可以求得

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn = 30005;int f[maxn],r[maxn],under[maxn];//我们要合并x和y的时候,要找y的最底的和x的最底的int findf(int k)//{  if(k==f[k]) return k;  int t=f[k];  f[k]=findf(f[k]);//这个不压缩的话,会导致MLE  under[k]+=under[t];  return f[k];}int main(){    int p;    scanf("%d",&p);    for(int i=0;i<maxn;i++)    {        f[i]=i;        r[i]=1;        under[i]=0;    }    for(int i=0;i<p;i++)    {        getchar();        char c;        scanf("%c",&c);        int a,b;        if(c=='M')        {            scanf("%d %d",&a,&b);            int root1=findf(a);            int root2=findf(b);            if(root1!=root2)            {                under[root1]=r[root2];                f[root1]=root2;                r[root2]+=r[root1];            }        }        else if(c=='C')        {            scanf("%d",&a);            int root=findf(a);            printf("%d\n",under[a]);        }    }    return 0;}
原创粉丝点击