poj1182食物链(种类,带权并查集)

题目大意:有三类不同动物,给出给K句话,描述两个动物间是同类还是捕食关系,若与前面的真话冲突,则判定为假话,输出假话的数量


思路分析:可用并查集将出现已知关系的动物合并在一起,各个动物到根结点距离对3的不同余数表示不同的种类,如对3的余数为0,1,2分别表示不同种的动物。当要将两物种合并在一起时,可通过修改其中一个跟结点的权值使这两种动物满足关系


 
 
#include<iostream>#include<stdio.h>using namespace std;int p[100000],A[100000],n;int f(int x){ if(x==p[x]) return x;
int F=f(p[x]); //路径压缩,同时更新该结点的权值,这条语句不可以和下面的更改顺序,要先更新其原父节点的值(初始化时为0)才能更新其值。因为下面每次更新时只更新了一个集合根结点的值,但集合中其他元素的值未变,所以查询一个点的权值,可从父节点一步步更新 A[x]=(A[p[x]]+A[x])%3; p[x]=F; return F;}int u(int x,int a,int b){ int f1=f(a),f2=f(b); if(a>n||b>n) return 0; if(x==1){ if(f1==f2&&A[a]!=A[b]) return 0; if(f1!=f2){ //当a和b不在同一个集合时,合并集合,同时修改a根结点f1的值,使合并后满足a和b同类 A[f1]=(A[b]-A[a]+3)%3; p[f1]=f2; } } else{ if(f1==f2){ if(A[a]==0&&A[b]!=1) return 0; if(A[a]==1&&A[b]!=2) return 0; if(A[a]==2&&A[b]!=0) return 0; } else{ //修改f1,使修改后满足a吃b, A[f1]=(A[b]-A[a]+2)%3; p[f1]=f2; } } return 1;}int main(){ int a,b,c,m,i,sum=0; scanf("%d%d",&n,&m); for(i=0;i<=60000;i++){ //初始化并查集,同时使所有的点初始为0 A[i]=0; p[i]=i; } for(i=1;i<=m;i++){ scanf("%d%d%d",&a,&b,&c); if(u(a,b,c)==0) sum++; } printf("%d\n",sum); return 0;}

猜你喜欢

转载自blog.csdn.net/guogai13/article/details/79683448