并查集在实际问题中的应用

并查集:用以将元素高效分组以及区分。

题目来源:codeforces 1012B

原问题如下,在一个(n*m)的table上,element会做出一种增值行为,如果有三个物质处于某个矩形的三个顶点上,那么在第四个顶点上会自动增值出一个element。现在table上已经存在了一些物质,求出最少仍需多少额外的物质,使得其可以将table覆盖。

对于解这道题来说,灵活的转化思想非常重要。首先我们可以证明,如果一个矩形有两条相邻的边被覆盖满,这个矩形就可以通过增值自行覆盖。进一步我们可以发现,如果将这两条边按垂直方向“打散”之后分散在矩形中,依然可以完成增值。

这个性质的本质是什么呢,可以将存在三个点(r1,c1),(r1,c2),(r2,c1),看作在(r1,c1),(r1,c2),(r2,c1)三对值之间存在联系,因而r2和c2也发生了联系,于是点(r2,c2)也随之存在,因此可以用并查集来解决问题,全集即为(r1,r2……rn,c1,c2……cn)。

每输入一个点,即将对应两个值unite起来;输入完毕后,会出现若干集合。这些集合内部的横纵坐标值自由组合形成的点就是已存在的;而集合之间所能组合形成的点都是尚未涂色的。最终我们希望达到的效果是,所有值都处在一个集合里,这样不论考查哪个点,它的横纵坐标一定是联系起来的。因此我们额外涂点的作用实际上,是将不同集合联系起来,因此结果等于集合数-1

附AC代码

 

#include
#include
using namespace std;

int p[400010],r[400010];

int fa(int ch)
{
    if(p[ch]==ch)
        return ch;
    return fa(p[ch]);
}

void unite(int u,int v)
{
    int fu=fa(u);
    int fv=fa(v);
    if(fu!=fv)
    {
        if(r[fv]==r[fu])
        {
            r[fu]++;
            p[fv]=fu;
        }
        else if(r[fu]>n>>m>>q;
    for(int i=1; i<=n+m; i++)
        p[i]=i;
    for(int i=0; i

 

你可能感兴趣的:(数据结构)