并查集知识点参考
主要应用的知识点:带权并查集的作用,如何维护带权并查集(本质是某个点沿多条能到根结点的路径时,这几条路径长度相等或在取模意义上相等)
题解
-
数据结构:由题得,所有元素(动物)都是有相互的关系且关系之间具有传递性,因为有关系可以考虑并查集,而有传递性可以考虑带权并查集。并查集用根节点维护,并查集中每个元素传递性关系用到根结点距离的相对关系维护。那就假定与父节点距离为1的节点是吃父节点的,0是同类,2是被父节点吃的,相对距离即为距离取模3后的值。即并查集确定有无关系,带权的距离确定是什么关系。
-
算法:对于每一对给定的关系,如果原先没有建立起关系,即原先没有在一个集合(图)中,那么由数学逻辑的知识可得,先去假定这对关系是正确的;如果这对关系已经在之前出现过,那么就判断这次的关系和之前的关系有没有矛盾,有矛盾就有一个谎话。
代码
#include <iostream>
using namespace std;
const int N = 5e4+5;
int p[N],d[N];//d代表某点到父节点的距离
int n,m;
int find(int x){
if(x!=p[x]){
int t=p[x];
p[x]=find(p[x]);
d[x]+=d[t];
}
return p[x];
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)p[i]=i;//初始化
int res=0;//记录谎话个数
while (m -- ){
int a,x,y;
cin>>a>>x>>y;
int px=find(x),py=find(y);
if(x>n||y>n)res++;//如果编号超出所给的动物编号数,直接判错
else{//在给定编号内,不管给出1还是2的关系,都按以下顺序来解题。
// 先判断是否已经在建立的图当中,如果在那么就可以判断是否为谎话,如果不在那么假定其不是谎话并加入图中
if(a==1){
if(px==py && (d[y]-d[x])%3)res++;//判断是否为谎话
if(px!=py){//假定为真话,加入图中
p[px]=py;
d[px]=d[y]-d[x];
}
}
else{
if(px==py && (d[y]+1-d[x])%3)res++;//判断是否为谎话
if(px!=py){//假定为真话,加入图中
p[px]=py;
d[px]=d[y]+1-d[x];
}
}
}
}
cout<<res<<'\n';
return 0;
}