背景
(CCF) (NOI) (2001) (Day1) (T1) , (Luogu) (P1955/Vijos1531)
题意
给定三种动物 (A,B,C) 共 (n) 只(现在不确定种类)。规定 (A) 吃 (B) , (B) 吃 (C) , (C) 吃 (A) ,给出 (n) 句话形如 (1 X Y(X,Y in [1,n])) 或者 (2 X Y(X,Y in [1,n])) ,前一种描述 (X) 和 (Y) 是同类。后一种描述 (X) 吃 (Y) 。规定一句话是假的的情况有三种:第一是给出的动物编号超过 (n) ,第二是当前这句话与比它早出现的话矛盾,第三是当前的话表示 (X) 吃 (X) 。求假话的数量。
解法
大名鼎鼎的扩展域并查集(种类并查集)出场啦!
因为关系只有吃、被吃和同类三种,考虑把每只动物 (x(x in [1,n])) 拆成仨集合 (x_{self},x_{enemy},x_{eat}) ,分别表示自己的同类集合、天敌集合以及食物集合,下标分别对应 ([1,n],[n+1,2n]) 以及 ([2n+1,3n]) 。
考虑怎么维护这三个集合。对于一句话,可以大力先把动物编号超过 (n) 的判掉,把表示 (X) 吃 (X) 的话判掉,于是问题就只有判断这句话与比它早出现的话矛盾了。考虑关系是按话给出的先后顺序维护的,于是只要它与已经形成的集合相违背就是假话。
然后问题就变成了对于一句真话咋维护集合。首先考虑 (X) 和 (Y) 是同类的情况,则两者的同类集合、天敌集合以及食物集合都是完全相同的,于是把两个元素的六个集合直接合并(即合并 (X_{self}) 与 (Y_{self}) , (X_{enemy}) 与 (Y_{enemy}) , (X_{eat}) 与 (Y_{eat}) )即可。然后是 (X) 吃 (Y) 的情况,根据题意有 (X) 吃 (Y) 时必有 (X) 是 (Y) 的天敌, (Y) 吃 (X) 的天敌,于是按题意合并即可(即合并 (X_{self}) 与 (Y_{enemy}) , (X_{enemy}) 与 (Y_{eat}) , (X_{eat}) 与 (Y_{self}) )。
最后再讲下假话的具体判断方法:不满足 (X) 和 (Y) 是同类的情况是 (X) 吃 (Y) 或者 (Y) 吃 (X) ,问题就转换成了判定 (X) 吃 (Y) 或者 (Y) 吃 (X) 是否存在,即判断 (X_{self}) 和 (Y_{eat}) 是否在同一集合、 (Y_{self}) 和 (X_{eat}) 是否在同一集合。不满足 (X) 吃 (Y) 情况是 (X) 和 (Y) 是同类或者 (Y) 吃 (X) ,问题就转换成了判定 (X) 和 (Y) 是同类或者 (Y) 吃 (X) 是否存在,即判断 (X_{self}) 和 (Y_{self}) 是否在同一集合、 (Y_{self}) 和 (X_{eat}) 是否在同一集合。