/*
*/
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【模板】冰茶姬(大概是全的?)

(好短!好用!)

点进来都是云里雾里的吧。。。先来介绍一下

冰茶姬(并查集)是个什么玩意?

(这篇博客主要介绍并查集,想知道冰茶姬是啥的出门左转)

并查集,顾名思义,分为“并”和“查”两个部分,是一种树型的数据结构,用于处理一些不相交集合的合并问题。

先从“查”谈起吧。

“查”

不是闰土刺的那一个啦。。

查询当前的两个节点的父亲是否相同,在查询的同时路径压缩。

路径压缩又是什么嘞。。

•判断两个元素是否属于同一集合只需要判断它们所在的树根是否相同,需要O(N)的时间来完成,于是路径压缩产生了作用
•路径压缩实际上是把一棵树的根节点设置为所有节点的父亲。在找完根结点之后,在递归回来的时候顺便把路径上元素的父亲指针都指向根结点
 所以路径压缩之后只需要O(1)的时间就可以查询出当前节点所对应的根节点

结合代码了解一下

int Getfather(int a)
{
    if(F[a]==a)
    return a;//如果当前节点的是根节点的话就返回 
    else
    F[a]=Getfather(F[a]);//若不是,就将他的父亲一层一层的向上找,知道找到最上面的根为止 (路径压缩) 
    return F[a]; 
}

这种是最简单的并查集啦。。还会有带权的。。会结合题目一起写在后面

“并”

当两个节点的父亲不同时,合并这两个节点。

结合代码看看

int find(int a,int b)
{
    int f1,f2;
    f1=Getfather(a);
    f2=Getfather(b);
    if(a==b)
    return 1;
    else
    {
        F[f1]=f2;//合并(你没有看错)
        return 0; 
    }
} 

(好简单。。)

看道题:(点击收获三倍经验)这个真是特别裸。。

模板如下:

#include<iostream>
using namespace std;
int F[50005];
int GF(int x)
{
    if(x==F[x])
    {
        return x;
    }
    else
    {
        F[x]=GF(F[x]);
        return F[x];
    }
}
void Merge(int x,int y)
{
    int fx=GF(x);
    int fy=GF(y);
    if(fx==fy)
    return;
    else
    {
        F[fx]=fy;
        return;
    }
    
}
int n,m,q;

int main()
{
    cin>>n>>m>>q;
    int x,y;
    for(int i=1;i<=n;i++)
    F[i]=i;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y;
        Merge(x,y);
    }
    for(int i=1;i<=q;i++)
    {
        cin>>x>>y;
        if(GF(x)==GF(y))
        cout<<"Yes"<<endl;
        else
        cout<<"No"<<endl;
    }
    return 0;
}

(但是真正比赛的时候肯定不会这么裸啊。。)

再来一道:(冰茶姬,你值得拥有) 这个其实也裸。。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int F[50001];
int GF(int x)
{
    if(F[x]==x)
    return x;
    F[x]=GF(F[x]);
    return F[x];
}
int main()
{
    int n,m;
    char A;
    int B,C;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n*2;i++)
    F[i]=i;
    for(int i=1;i<=m;i++)
    {
        cin>>A;
        scanf("%d%d",&B,&C);
        if(A=='F')
        F[GF(B)]=F[GF(C)];
        if(A=='E')
        {
            F[GF(B+n)]=F[GF(C)];
            F[GF(C+n)]=F[GF(B)];
        }
    }
    int Ans=0;
    for(int i=1;i<=n;i++)
    {
        if(F[i]==i)
        Ans++;
    }
    printf("%d",Ans);
    return 0;
} 

 

来一道不那么裸的吧。。。俄罗斯方块++版

这时直接用上面的模板就有问题了。。要怎么统计方块数呢?直接用路径压缩的话就会直接把方块数压缩掉。。。

先想这么几个问题

怎么判断x,y是否在一个柱子上?

怎么合并x,y所在的柱子?

怎么计算x之前的方块个数?

怎么计算x所在柱子的总方块数?

很明显,前两个直接用上文的模板就可以直接搞定,后面两个就不行了。。只用一个Father数组是解决不了这么多问题的。。

我们不妨设x到x的根节点一共有before[x]个方块,x所在的柱子一共有count[x]个方块

这样就很容易得出第四个问题的答案,而最后一个问题,用count[x]-before[x]-1也可以解决(这个地方看不懂就自己画个图,秒懂)

这其实就是较简单的带权并查集啦。。

(还有一题比较难的。。等我看懂了再单独写一篇题解吧。。)

(伪链接,后续会有的。。。)

下面是代码:

int Getfather(int a)
{
    int dad;
    if(F[a]==a)
    return a;//如果当前节点的是根节点的话就返回 
    else
    {
    dad=Getfather(F[a]);
    before[a]+=before[F[a]];//将当前节点到根节点的路径累加起来 
    F[a]=dad;
    return F[a]; 
    }
}
void find(int a,int b)
{
    int f1,f2;
    f1=Getfather(a);
    f2=Getfather(b);
    if(a==b)
    return;
    else
    {
        F[f2]=f1;//f1放在f2的上方,将f2合并到f1所在的集合 
        count[f1]=count[f2]+count[f1];
        before[f2]=before[f2]+count[f1];
    }
} 

只是核心代码啦。。想要经验的话就自己加输入输出好啦。。(小声)

(关于上面的那道很难的题。。我会尽力写啦。。不会鸽掉的)

(RP++!)

posted @ 2019-09-22 21:35  Kyoko_Yosa  阅读(272)  评论(1编辑  收藏  举报
浏览器标题切换
浏览器标题切换end