[算法模版]种类并查集
前言
众所周知,基本的并查集维护很多“集合”,同时可以对集合进行合并操作。而同一集合内的元素性质完全相等。但是如果我们不只需要维护相同的元素,还要维护不同元素之间的关系。那我们就可以使用种类并查集来处理。
基本思想
因为同一集合内元素性质完全相等,所以当集合中的一个元素确定了对另一个元素的关系时,我们也确定了这两个元素所在集合之间的关系。而在之前的普通并查集中,两个元素在同一个集合内代表他们完全相等。而这里我们需要更改这个规则。
我们先把基础并查集中每个集合的元素都称作(A)类点,当我们需要维护两个集合之间某种关系时,我们就可以引入一种新的点——(B)类点。如果两个(A)类点处于同一集合,就代表他们完全相等,而如果一个(A)类点和一个(B)类点处于同一集合,就代表他们是另一种关系。这样我们就可以维护集合间的关系了。
或者换种说法,每个点其实代表了所在集合的“性质”。性质相同的点会被合并。当你需要查找跟当前点是某种关系的集合时,只需要查找对应点所在的集合即可。
例题
新建立两类点,并给他们分配新的编号来识别种类。(Bin[n+1,2n],Cin[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;
}
注意事项
需要注意的是,在查询的过程中,可以查询任意一个符合的条件即可(因为所有能推出的信息都已经推出)。而合并的时候则要合并所有能推出的信息。