[算法模版]种类并查集

[算法模版]种类并查集

前言

众所周知,基本的并查集维护很多“集合”,同时可以对集合进行合并操作。而同一集合内的元素性质完全相等。但是如果我们不只需要维护相同的元素,还要维护不同元素之间的关系。那我们就可以使用种类并查集来处理。

基本思想

因为同一集合内元素性质完全相等,所以当集合中的一个元素确定了对另一个元素的关系时,我们也确定了这两个元素所在集合之间的关系。而在之前的普通并查集中,两个元素在同一个集合内代表他们完全相等。而这里我们需要更改这个规则。

我们先把基础并查集中每个集合的元素都称作\(A\)类点,当我们需要维护两个集合之间某种关系时,我们就可以引入一种新的点——\(B\)类点。如果两个\(A\)类点处于同一集合,就代表他们完全相等,而如果一个\(A\)类点和一个\(B\)类点处于同一集合,就代表他们是另一种关系。这样我们就可以维护集合间的关系了。


或者换种说法,每个点其实代表了所在集合的“性质”。性质相同的点会被合并。当你需要查找跟当前点是某种关系的集合时,只需要查找对应点所在的集合即可。

例题

NOI2001-食物链

新建立两类点,并给他们分配新的编号来识别种类。\(B\in[n+1,2n],C\in[2n+1,3n]\)

当一个\(A\)类点和一个\(B\)类点处于同一集合,则代表\(A\)类点可以攻击\(B\)类点。而一个\(A\)类点和一个\(C\)类点处于同一集合,则代表\(A\)类点会被\(C\)类点攻击。

#include<iostream>
using namespace std;
const int maxn=5e4+1000;
int fa[maxn*3],n,k,ans;
int get(int x) {
    if(fa[x]==x)return x;
    fa[x]=get(fa[x]);
    return fa[x];
}
void uni(int x,int y) {
    fa[get(x)]=get(y);
}
int main() {
    ios::sync_with_stdio(0);
    cin>>n>>k;
    for(int i=1;i<=3*n;i++)fa[i]=i;
    for(int i=1;i<=k;i++) {
        int ty,x,y;cin>>ty>>x>>y;
        if(x>n||y>n){ans++;continue;}
        if(ty==1) {
            if(get(x)==get(2*n+y)||get(x)==get(n+y)) {ans++;continue;}
            else {
                uni(x,y);uni(x+n,y+n);uni(y+2*n,x+2*n);
            }
        }
        else {
            if(get(x)==get(y)||get(x)==get(y+n)){ans++;continue;}
            uni(n+x,y);uni(2*n+y,x);uni(2*n+x,n+y);
        }
    }
    cout<<ans;
}

注意事项

需要注意的是,在查询的过程中,可以查询任意一个符合的条件即可(因为所有能推出的信息都已经推出)。而合并的时候则要合并所有能推出的信息。

posted @ 2019-10-25 17:14  GavinZheng  阅读(458)  评论(0编辑  收藏  举报