【带权并查集】POJ1988

来源:互联网 发布:网络系统集成书籍 编辑:程序博客网 时间:2024/05/23 01:58

题目链接在这里
题目大概的意思就是两种操作,一种是把含有x的整个栈放到含有y的整个栈上面。另外一种是问x下面有多少个物品。
物品数目是30,000以内,操作次数在100,000以内。

一开始的时候我们想的是直接模拟?这个毫无疑问是不可以的。
但是一眼看过去,似乎和并查集没什么关系?

这个时候,我们从栈开始分析。计算一个物品下面有多少个物品,那么有两种计算方法:

  1. 直接计算。
  2. 栈里面的元素个数-当前距离栈顶的距离。

    我们在这里考虑算下面的个数?有没有什么好办法?(反正我不会)
    我在做这个题目的时候用的是第二种方法。

如果我们依旧是一个栈的话,看成是一个退化成链的树,那么当前结点距离栈顶的距离很容易就能看明白就是树的深度。

(重点来了)
对于操作,我们看到修改操作是把两个子树合并到一起。
所以子树的结点对于子树的根节点的相对距离是不变的。
所以这条链像并查集那样压缩也是可以的(只不过压缩路径之后,距离不改变)。
那么合并是怎么做的呢?我们假设子树A,B。 现在把A放到B上面。
那么实际上就是A成为了子树B的根。按照更新,子树B上面的点记录的距离要全部加上A原来的大小。
因为点是乱叠来叠去的,所以区间更新搞不了了。怎么办?
这个时候并查集的作用来了。只需要在B的根节点上面加上A的大小就行了。
为什么?
因为B的结点都是指向B的根节点,一旦询问B上面的结点的话,从B开始往上找一下根节点,在路径压缩的过程中把距离给加上去就好了。
举个例子,A的大小为5,根节点为a,B的根节点b,B上面有一个点x,x距离B的根节点是deep【x】。 那么我们本来是x->b->a
其中x到b的深度为deep【x】,b到a的深度为5,那么我们找爸爸的时候 路径变成了x->a,b->a 此时deep[x]=5+deep[x];当然如果是有很多个原来是根节点的话,就由大家一起脑补了,实际上递归从上开始更新就好了。

代码如下:

#include<cstdio>#include<cstring>#include<iostream>using namespace std;#define MAXN 30000 int cnt[MAXN+10];int dep[MAXN+10];int fa[MAXN+10];void init(int n){    for(int i=0;i<=n;i++)    {        fa[i]=i;        cnt[i]=1;        dep[i]=0;    }}int findfa(int x,int &cou){    if(fa[x]==x)    {        cou=0;        return x;    }    fa[x]=findfa(fa[x],cou);    dep[x]=dep[x]+cou;    cou=dep[x];    return fa[x];}int main(){    int p;    scanf("%d",&p);    init(MAXN);    while(p--)    {        char sign;        scanf(" %c",&sign);        if(sign=='M')        {            int x,y;            int cou;            scanf("%d%d",&x,&y);            int yy=findfa(y,cou);            int xx=findfa(x,cou);            fa[yy]=xx;            dep[yy]+=cnt[xx];            cnt[xx]+=cnt[yy];        }        else        {            int x,cou;            scanf("%d",&x);            int xx=findfa(x,cou);            printf("%d\n",cnt[xx]-dep[x]-1);         }     }    return 0;}   
0 0
原创粉丝点击