• 蒟蒻林荫小复习——带权并查集


    实际上很早之前林荫是有这个技能的。(废话!要不直接叫小学习好了)

    众所周知,并查集可以用来维护一些元素之间相连的关系(不知道的出门右转幼儿园)

    而状态压缩可以使得并查集查询一对元素的关系的速度变快(O1)

    状态压缩之后的并查集实际上是一个由fa数组(相当于单向链表)构成的菊花图

    那么,如何用并查集来维护元素之间实际的数量关系呢?

    先看一个例子:

    假定现在有3个小朋友ABC,B比A大3岁,C比B大2岁。对于这个问题,如果用不带状压的并查集进行表示,那么就可以得到一条链。B到A的边权3表示

    B比A大3岁,C到B的边权2表示C比B大2岁。好的我们现在询问C和A的年龄关系。如果一个一个查询这条链上的点那么一定很慢。

    是否可以考虑一下,让ABC三者的年龄都与某个人的年龄相关呢(实际上这时就是尝试转化为菊花图)

    我们可以考虑让C直接与A相连,但是边权怎么办?

    实际上C和A的年龄差就是C和B的差加上B和A的差。那么我们就让菊花图中A——C的边权为dis[B][A]+dis[C][B]

    这样的话,我们就可以做到O1查询两个点之间的数量关系了(就比如询问CB年龄差实际上就是CA减去BA)

    那么实际上代码也不难实现:

    int Find(int x)
    {
        if(x==fa[x])
            return x;
        int t=Find(fa[x]);
        dis[x]+=dis[fa[x]];
        fa[x]=t;
        return t;
    } 

    如果找到最终父亲(就代表这个点是和菊花图的花心相连不用改)直接返回

    否则和自己的权就和自己的父亲相加(因为这个时候自己的父亲已经链接到花心上了,权也是父亲和花心的关系)求出自己和花心的关系

    至于t为啥一直都是菊花图花心(最终父亲),因为t一直都是Find(fa[x])的结果,fa[x]在对x进行修改时,要么本身就是花心,要么已经和花心直接相连。

    下面来两道练手水题:

    T1:洛谷P1196NOI2002银河英雄传说

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int dis[30001];//每艘战舰到所在行的旗舰中间所隔战舰数 
    int len[30001];//每艘战舰所在行的战舰总数
    int fa[30001];
    int Find(int x)
    {
        if(x==fa[x])
            return x;
        int t=Find(fa[x]);
        //先一步步跟到最终父节点
        dis[x]+=dis[fa[x]];//假定fa[fa[x]]为最终父节点,那么dis[fa[x]]就相当于直接连接到菊花图花心的边权,dis[x]+=dis[fa[x]]相当于将x从fa[x]直接链接到fa[fa[x]] 
        fa[x]=t;//这时再把x直接链接到菊花图的花心 
        return fa[x];
    } 
    void Move(int x,int y)
    {
        int fx=Find(x);
        int fy=Find(y);
        dis[fx]+=len[fy];
        len[fy]+=len[fx];
        fa[fx]=fy;
    }
    int ABS(int x)
    {
        return max(x,-x);
    }
    char x[1];
    int a1,a2;
    int LINYIN()
    {
        int T;
        scanf("%d",&T);
        for(int i=1;i<=30000;i++)
        {
            fa[i]=i;
            len[i]=1;
            dis[i]=0;
        }
        while(T--)
        {
            scanf("%s%d%d",x,&a1,&a2);
            if(x[0]=='M')
            {
                Move(a1,a2);
            }
            else
            {
                if(Find(a1)!=Find(a2))
                {
                    printf("%d
    ",-1);
                }
                else
                {
                    printf("%d
    ",ABS(dis[a1]-dis[a2])-1);
                } 
            }
        }
        return 0;
    } 
    int LWH=LINYIN();
    int main()
    {
        ;
    }
    View Code

    T2:洛谷P5092方块游戏

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int len[30001];
    int dis[30001];
    int fa[30001];
    int Find(int x)
    {
        if(x==fa[x])
            return x;
        int t=Find(fa[x]);
        dis[x]+=dis[fa[x]];
        fa[x]=t;
        return t;
    } 
    void Move(int x,int y)
    {
        int fx=Find(x);
        int fy=Find(y);
        fa[fy]=fx;
        dis[fy]=len[fx];
        len[fx]+=len[fy];     
    }
    int n,a1,a2;
    char k[1];
    int LINYIN()
    {
        scanf("%d",&n);
        for(int i=1;i<=30000;i++)
        {
            dis[i]=0;
            fa[i]=i;
            len[i]=1;
        }
        for(int i=1;i<=n;i++)
        {
            scanf("%s",k);
            if(k[0]=='M')
            {
                scanf("%d%d",&a1,&a2);
                Move(a1,a2);
            }
            else
            {
                scanf("%d",&a1);
                int fx=Find(a1);
                printf("%d
    ",len[fx]-(dis[a1]+1));
                //cout<<len[fx]-(dis[a1]+1)<<endl;
            }
        }
        return 0;
    } 
    int LWH=LINYIN();
    int main()
    {
        ;
    }
    View Code

    完结撒花!!!

  • 相关阅读:
    纪中第三天
    纪中第一天
    图片验证码的实现
    使用监听器解决路径问题
    log4j测试示例
    redis示例
    kafka示例
    CSRF verification failed. Request aborted.
    TemplateDoesNotExist
    创建 django 项目命令
  • 原文地址:https://www.cnblogs.com/XLINYIN/p/11706283.html
Copyright © 2020-2023  润新知