题目链接
http://acm.hdu.edu.cn/showproblem.php?pid=3038
根据前面的回答判断下一个回答是否矛盾,因为判断下一个回答要根据前面的回答,所以用并查集来记录前面数之间的关系,如果根据前面数的关系不能判断出这组数据的答案,那就把这组数据也加入到并查集里,如果能判断出答案,但是答案错误,那么res++,下面举一个栗子
如果我在前面有输入a,c和a,b之间的关系,在这一组数据中输入了b,c的关系,用V(a,b)代表a,b之间的距离,那么就有V(a,c)-V(a,b)==V(b,c),如果不符合,res++
输入三个数digit1,digit2,distance,就是[digit1,digit2]的和为distance,可以理解为digit2比(digit1-1)这个数大distance,用sum数组来储存权值"distance",这里就有sum[digit1-1]=distance。
但是在并查集查找的时候,不但要做路径压缩,压缩的同时还要维护权值数组sum的值,这里要用到向量的知识。举个例子,digit1的祖先是x,digit2的祖先是y,这里默认并查集合并时,x并到y上,但是光并上去不行啊,还得计算sum[x]的值,如何计算?看下面一张图
在查找d1和d2的祖先的过程中,我们肯定知道[d1,x]和[d2,y]的权值,为什么要合并x和y?出现这种情况就是因为输入给出了d1,d2之间的关系,而且这种关系无法判断真假,按真处理,加入并查集,那么如何计算sum[x]?这时候就很明显了,sum[x]就是[x,y]的和,通过向量,仍用V(a,b)来表示[a,b]之间的权值,V(x,y)=V(d2,y)+V(d1,d2)-V(d1,x)。
在d1,d2祖先相同的时候,如何判断他们的权值V(d1,d2)是否正确呢?也是这个道理,看下面一张图,因为他们有公共祖先,证明他们的sum值肯定在查找祖先的过程中经过了维护,直接指向了y(这里假设y为公共祖先),就有
V(D1,y)-V(D2,y)==V(D1,D2),V(D1,D2)的值会在这组输入数据中给出,根据这个进行判断即可。
C
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4996)
int tree[200001],sum[200001];
int get(int digit)
{
int root;
if(tree[digit]==digit)
return digit;
root=get(tree[digit]);
sum[digit]+=sum[tree[digit]];
return tree[digit]=root;
}
int main(void)
{
int N,M,i,res,digit1,digit2,distance,x,y;
while(scanf("%d %d",&N,&M)!=EOF)
{
memset(sum,0,sizeof(sum));
for(i=0;i<=N;i++) //注意这里的初始化,要从0开始,因为下面有digit1--,这里卡了我1小时WA
tree[i]=i;
for(res=0,i=0;i<M;i++)
{
scanf("%d %d %d",&digit1,&digit2,&distance);
digit1--;//注意这里,[d1,d2]的距离可以理解为d2比d1-1这个数大distance
x=get(digit1);
y=get(digit2);
if(x!=y) //如果祖先不相同,无法判断,一律按正确处理,并入并查集,我这里以默认x是y的后代,如果y是x的后代,向量计算将会改变,要注意表达式的变化
{
tree[x]=y;
sum[x]=distance+sum[digit2]-sum[digit1];
}
else
if(sum[digit1]-sum[digit2]!=distance)
res++;
}
printf("%d\n",res);
}
return 0;
}