題目鏈接
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;
}