带权并查集+种类并查集

好久没写博客了,差点忘了在哪儿写,不写博客是真的对学过的东西一点印象都没有T_T;
丧心病狂的大佬挂了一道种类并查集+dp的题,然而我却连种类并查集是什么都不知道,赶紧学习缩小差距;
1.带权并查集(对这两个的关系还有点儿懵,先上再说)
就像名字一样,带权并查集就是要多维护一个权值数组;和普通的并查集大同小异,用f[]数组存父节点,val存当前结点到父亲结点的权值,
首先是初始化

void Initial()
{
    for(int i=1;i<=n;i++)  //n是总数啦
    {
        f[i]=i;val[i]=0; 
    }
}

先来不状态压缩的在这里插入图片描述没有压缩时可以知道f[3]=2,f[2]=1,f[1]=1;val[3]=4;val[2]=3;val[1]=0;
状态压缩后:在这里插入图片描述f[3]=1;val[3]=7;其他不变;所以可以得出代码如下

int Find(int x)
{
    if(f[x]==x)
        return f[x];
    int tmp=Find(f[x]);  //这里不能够没有,因为在递归的过程中f[x]的值会被改变,会影响val[x]的更新
    val[x]=val[x]+val[f[x]];
    return f[x]=tmp;
}
而对于合并两个集合,是将元素间关系看成向量
我们可以列出向量图

在这里插入图片描述我们假设上图中2->1表示1比2大3,同理得其他的,一开始1,2处于一个集合,3,处于另一个集合,现在要将这两个集合合并,那么1->4得权值就可以得出,根据平行四边形法则,val[1]=(-val[2]+v+val[3])=8(其中v表示3比2大多少有,也就是2->3),就是说4比1大8,由此我们便得到了如何将两个带权集合合并;

void Union(int a,int b)
{
    int fa=Find(a),fb=Find(b);
    if(fa!=fb)
    {
        f[fa]=fb;
        val[fa]=-val[a]+v+val[b];
    }
}  //带权并查集Over

例题:How Many Answers Are Wrong HDU - 3038
2.种类并查集
我觉得种类并查集延用了带权并查集的特性,还是相当于维护了一个权值,只不过这个权值带有一定的意义,拿例题“食物链”来说,我们指定x->y表示与y是x的父节点,即father[x]=y;我们用rela[i]来表示节点i和它的父节点之间的关系,规定rela[x]=0表示x,y是同类,rela[x]=1表示y吃x,rela[x]=2表示x吃y,那么我们可以根据这些权值推断出两个结点之间实际的关系。
在这里插入图片描述
比如现在告诉你3->1的值为1(1吃3),而2->1的值为2(2吃1),那么我们根据题意就可以得出3吃2,即2->3为1,也就得到了上图的关系。假设现在再来一句话说2吃3那么就是错的。问题是如何得到这样的关系。对于给定的x,y和它们之间的关系,我们分两种情况
1.假设father[x]==father[y],就是说它们是一个集合的,现在的任务就是把它们推出它们之间的关系,也就是上图的情况,1为父节点,先开始的条件有3->1的为1,2->1=2,如何得到2->3=1呢?同样我们可以用向量的方法:2->3=2->1 - 1->3(添负号,箭头反向)=2-1=1;这样我们得到了2->3的值为1(这里在想一下,如果我们想得到假设之前我们已经建立了2->1的关系,现在又给出2->1的关系,如何得到到给出的关系对不对呢;我们只需要把上图的3换成1就可以了,同时3->1=1就变成了1->1=0同样的方法)
2,假设father[x]!=father[y],x,y不在同一个集合也就是要联合x,y两个集合,已知y->x=1,x->fx=1,y->fy=0
在这里插入图片描述同样的用向量就可以了fy->fx=-(y->fy)+y->x+x->fx=1+1=2;(已知fx吃x,x吃y,y和fy是同类,所以由题意可知fy吃fx),这样我们就可以将两个集合联合在一起并且维护好关系。
值得注意的是,这些操作对向量方向的要求很高,不能弄错方向,比如联合两个集合的时候,我们求的是fy->fx,也就是fy是儿子,fx是父亲,那么我们就应该father[fy]=fx;而不是father[fx]=fy;同样我们求的关系是rela[fy]的值,而不是rela[fx],如果箭头反向,这些操作都要变.
写的差不多了,其实我对种类并查集的种类还是不怎么明白,但如果这样想一想:子节点和父节点的权值为0表示两者同类,为1表示两者异类,不就可以判断两个结点是不是同一类了的吗,权值的取值更多也就代表结点之间的关系更加的多样化,这不就有点儿种类的味道了嘛(我们的前辈们真是有点儿东西的啊^ _ ^);
食物链

#include <iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define lson (rt<<1)
#define rson (rt<<1|1)
const int inf_max=0x3f3f3f;
const int maxn = 100000+10;
typedef long long ll;
using namespace std;
int n,k,father[maxn],rela[maxn];   
int Find(int x)
{
    if(x==father[x])
        return father[x];
    int tmp=Find(father[x]);
    rela[x]=(rela[x]+rela[father[x]])%3; 
    return father[x]=tmp;   //状态压缩
}
void Initial()
{
    for(int i=0;i<=n;i++)
    {
        father[i]=i;
        rela[i]=0;
    }
}
int main()
{
    scanf("%d%d",&n,&k);
        Initial();
        int ans=0;
        for(int i=1;i<=k;i++)
        {
            int re,x,y;
            scanf("%d%d%d",&re,&x,&y);
            if((re==2&&x==y)||x>n||y>n)
            {
                //printf("this is lie\n");
                ans++;
                continue;
            }
            re--;
            int fx=Find(x),fy=Find(y);
            if(fx!=fy)
            {
                father[fx]=fy;
                rela[fx]=(-rela[x]+rela[y]-re+3)%3;
            }
            else if((rela[y]-rela[x]+3)%3!=re)
            {
                //printf("rela[%d]=%d,rela[%d]=%d,this is lie\n",x,rela[x],y,rela[y]);
                ans++;
            }
        }
//        for(int i=1;i<=3;i++)
//            printf("father[%d]=%d\n",i,father[i]);
        printf("%d\n",ans);
    return 0;
}
发布了33 篇原创文章 · 获赞 14 · 访问量 442

猜你喜欢

转载自blog.csdn.net/qq_44077455/article/details/102526974