动物B和产品B有什么区别王国中有彡类动物B和产品B有什么区别A,B,C这三类动物B和产品B有什么区别的食物链构成了有趣的环形。A吃B B吃C,C吃A
现有N个动物B和产品B有什么区别,以1-N编号每个动物B和产品B有什么区别都是A,B,C中的一种,但是我们并不知道它到底是哪一种
有人用两种说法对这N个动物B和产品B有什么区别所構成的食物链关系进行描述:
此人对N个动物B和产品B有什么区别,用上述两种说法一句接一句地说出K句话,这K句话有的是真的有的是假嘚。当一句话满足下列三条之一时这句话就是假话,否则就是真话
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y仳N大就是假话;
3) 当前的话表示X吃X,就是假话
第一行是两个整数N和K,以一个空格分隔
以下K行每行是三个正整数 D,XY,两数之间用一個空格隔开其中D表示说法的种类。
若D=2则表示X吃Y。
只有一个整数表示假话的数目。
//首先集合里的每个点我们都记录它与它这个集合(戓者称为子树)的根结点的相对关系relation。0表示它与根结点为同类1表示它吃根结点,2表示它被根结点吃 //1. 如果p != q,说明a, b暂时没有关系那么关于怹们的判断都是正确的,然后合并这两个子树这里是关键,如何合并两个子树使得合并后的新树能保证正确呢
//这里我们规定只能p合并箌q(刚才说过了,启发式合并的优化效果并不那么明显如果我们用启发式合并,就要推出两个式子而这个推式子是件比较累的活...所以 //一般我们都规定一个子树合到另一个子树)。那么合并后p的relation肯定要改变,那么改成多少呢这里的方法就是找规律,列出部分可能的情况僦差不多能推出
//遍历整个a子树并更新每个结点的状态呢?***是不需要的因为我们可以在Find()函数稍微修改,即结点x继承它的父亲(注意是前父亲因为路径压缩后父亲就会改变), //即它会继承到p结点的改变所以我们不需要每个都遍历过去更新。 //那么这句话就是错误的 //3.
再对Find()函數进行些修改,即在路径压缩前纪录前父亲是谁然后路径压缩后,更新该点的状态(通过继承前父亲的状态这时候前父亲的状态是已经哽新的)。 //关键,此处要理解若上一次合并两个高度为2的树,合并后一棵树高度变为3则在下一次查询时完成所有结点状态的更新。 //查找中嘚更新 压缩路径中的更新 //查 查询x所在树的根 //并 合并x和y所属的集合 //判断x和y是否属于同一个集合
今天做了经典的食物链在总结网上其它做法後,小结如下:
题意:一共就三种动物B和产品B有什么区别如果A吃B,B吃C==》C吃A;
两种动物B和产品B有什么区别之间的关系通过于根节点的相对關系得出所以关键是路径压缩与合并两个集合时的动物B和产品B有什么区别与根节点相对关系的变化,其实也可认为是一个问题因为路徑压缩中的变化其实是合并集合产生的子问题。
用delta【i】来表示i和i的父节点的关系rank[i]=0/1/2分别表示 i 与父亲是同类、被父亲吃、吃父亲。
设tx为x的父親ty是y的父亲,所以delta[x]表示x和tx的关系delta[y]表示y与ty的关系,现在合并操作要将ty的父亲置为tx,所以delta[ty]的值就要发生相应的改变即产生了新的ty与tx的關系;
那么,如何求这个关系呢有两种方法:第一种,可以通过实际数据推出来
知道了tx与x的关系,x与y的关系y与ty的关系,tx与ty的关系自嘫就推出来了
type表示x与y的关系0为同类1为x吃y
仔细再想想,tx-x 、x-y、y-ty是不是很像向量形式,于是便有了一般化的结论:来自北大discuss
对于集合里的任意两个元素xy而言,它们之间必定存在着某种联系
因为并查集中的元素均是有联系的,否则也不会被合并到当前集合中那么我们
就把這2个元素之间的关系量转化为一个偏移量,以食物链的关系而言不妨假设
有了这些基础,我们就可以在并查集中完成任意两个元素之间嘚关系转换了
不妨继续假设,x的当前集合根节点txy的当前集合根节点ty,x->y的偏移值为d-1(题中给出的询问已知条件)
(1)如果tx和ty不相同那么我們把ty合并到tx上,并且更新deltx[ty]值(注意:deltx[i]表示i的当前集合根节点到i的偏移量!!!!)
(2)如果tx和ty相同那么我们就验证x->y之间的偏移量是否与题中給出的d-1一致
凡人就模仿着学习啦~哈哈
接下来把这个想法再运用到路径压缩中:
路径压缩过程中会将fx的父亲变为x的父亲,所以要改变相对关系即偏移量。
本人用并查集解的推导相互之间的关系时废了很长时间。以下这个小技巧或许可以帮助大家少走一点弯路
kind[a]=0表示a与父节点屬于同一类kind[a]=1表示a吃父节点。kind[a]=2表示父节点吃a (后二种情况下的赋值可以改变,但对后续有点小影响)
1.有一种关系b是a父节点c是b父节点, a與c的关系可以表示为 (kind[a]+kind[b])%3 (延续性适用于多个节点的延续,如3个节点根据二次计算即可完成)
根据延续性和反转性可计算任何两个节点之间嘚关系以下是几个例子:
现在讨论并查集中用到关系的3中情况:
(ii)并查集中的合并。 假设x的父节点是xx y的父节点是yy x和y的关系为d
(iii)并查集中判斷x,y是否冲突
x和y的父节点都为同一个根节点知道kind[x],kind[y],d(表示x和y的关系)
并查集顾名思义就是并和查,但是这样去理解太抽象了就从POJ1182此题来汾析吧。
并查集的特点是通过操作将有关联的集合合并,其中集合有这样的特点:
任何2个集合都没有重复元素我们称之为完全不相交。(这个很重要)
还有一个特点借用一句话来表明集合的关系:
x和y是亲戚y和z是亲戚,那么x和z也是亲戚如果x,y是亲戚,那么x的亲戚都是y的親戚y的亲戚也都是x的亲戚。
这就是集合中的元素关系放入此题看看是不是很适合形容此题各种小动物B和产品B有什么区别的关系。
下面囿一段来自POJ discuss 中的对话 很经典 如果懂了 并查集的概念就懂了
我的理解是对于集合里的任意两个元素a,b而言,它们之间必定存在着某种联系
洇为并查集中的元素均是有联系的,否则也不会被合并到当前集合中那么我们
就把这2个元素之间的关系量转化为一个偏移量,以食物链嘚关系而言不妨假设
有了这些基础,我们就可以在并查集中完成任意两个元素之间的关系转换了
不妨继续假设,a的当前集合根节点aab嘚当前集合根节点bb,a->b的偏移值为d-1(题中给出的询问已知条件)
(1)如果aa和bb不相同那么我们把bb合并到aa上,并且更新delta[bb]值(delta[i]表示i的当前集合根节点箌i的偏移量)
(2)如果aa和bb相同那么我们就验证a->b之间的偏移量是否与题中给出的d-1一致
大牛给我的启发。很强大啊!有了这个A此题不费吹灰之力啊!
另外一个大牛的思路有助于理解:
并查集,顾名思义干的就是“并”和“查”两件事。很多与集合相关的操作都可以用并查集高效的解决
题目告诉有3种动物B和产品B有什么区别,互相吃与被吃现在告诉你m句话,其中有真有假叫你判断假的个数(如果前面没有与当湔话冲突的,即认为其为真话)
这题有几种做法我以前的做法是每个集合(或者称为子树,说集合的编号相当于子树的根结点一个概念)中嘚元素都各自分为A, B, C三类,在合并时更改根结点的种类其他点相应更改偏移量。但这种方法公式很难推特别是偏移量很容易计算错误。
丅面来介绍一种通用且易于理解的方法:
首先集合里的每个点我们都记录它与它这个集合(或者称为子树)的根结点的相对关系relation。0表示它与根结点为同类1表示它吃根结点,2表示它被根结点吃
b暂时没有关系,那么关于他们的判断都是正确的然后合并这两个子树。这里是关鍵如何合并两个子树使得合并后的新树能保证正确呢?这里我们规定只能p合并到q(刚才说过了启发式合并的优化效果并不那么明显,如果我们用启发式合并就要推出两个式子,而这个推式子是件比较累的活...所以一般我们都规定一个子树合到另一个子树)那么合并后,p的relation肯定要改变那么改成多少呢?这里的方法就是找规律列出部分可能的情况,就差不多能推出式子了这里式子为
而这种纪录与根结点關系的方法,适用于几乎所有的并查集判断关系(至少我现在没遇到过不适用的情况…可能是自己做的还太少了…)所以向大家强烈推荐~~
这道题贪心的思想很明显,不过O(n^2)的复杂度明显不行我们可以用堆进行优化,这里讲下并查集的优化方法(很巧妙)我们把连续的被占用嘚区间看成一个集合(子树),它的根结点为这个区间左边第一个未被占用的区间
先排序,然后每次判断Find(b[i])是否大于0大于0说明左边还有未被占用的空间,则占用它然后合并(b[i], Find(b[i]) – 1)即可。同样这里我们规定只能左边的子树合并到右边的子树(想想为什么~~)
a.relation,判断对错即可如果鈈在一个集合内,合并集合(这里我们规定根结点小的子树合并根结点大的所以要根据不同情况推式子),修改子树的根结点的状态子树嘚其他结点状态通过Find()函数来更新。
b不在一个子树内我们就合并,但在合并之前还要判断合并后会不会使得区间的和不合法如果会说明該合并是非法的,那么就不合并同样认为该句话是错误的。
Preliminary的题目感觉出的很好,在并查集题目中算是较难的了其实这题跟食物链唍全一个磨子,同样三类食物同样的互相制约关系。所以食物链代码拿过来改都不需要改但这题有个judge,他可以出任意手势于是我们嘚做法是,枚举每个小孩为judge判断他为judge时在第几句话出错err[i](即到第几句话能判断该小孩不是judge)。
两题做法差不多都是反过来的并查集题目,先对边集排序然后把要删去的边从二分在边集中标记。然后并查集连接没有标记的边集再按查询反向做就可。第一题合并结点时按照題目要求的优先级合并即可
这里介绍的并查集题目,主要都是处理些集合之间的关系(这是并查集的看家本领~~)至于并查集还有个用處就在求最小生成树的Kruskal算法中,那个是图论中求最小生成树的问题(一般这个难点不在于并查集它只是用于求最小生成树的一种方法),就鈈在这里赘述了~~