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

Posted xlinyin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蒟蒻林荫小复习——带权并查集相关的知识,希望对你有一定的参考价值。

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

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

而状态压缩可以使得并查集查询一对元素的关系的速度变快(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

完结撒花!!!

以上是关于蒟蒻林荫小复习——带权并查集的主要内容,如果未能解决你的问题,请参考以下文章

蒟蒻林荫小复习——K短路的A*解法

蒟蒻林荫的小复习——主席树

蒟蒻林荫小复习——关于有限制区间元素查询的一些解法

「带权并查集」奇偶游戏

CF553C Love Triangles(带权并查集)

带权并查集——食物链