2019-07-10,在某人的不懈努力之下,某人真正弄懂种类并查集了,可喜可贺
种类并查集,是什么东西,我也不知道,我就是在看别人题解时提到过这个名字,我粗略解释一下,就是带了种类的并查集???
好吧,这都不重要,重要的是,种类并查集到底该怎么用。
-
种类并查集一般会分为几个种类,根据题目,有时会分为两类,即同类和敌人,在[NOI2001]食物链
中分为了三类,分别是同类,食物和天敌。 -
种类并查集一般数组要相应开到几倍,初始化时也要循环到几倍。
-
一般根据题意,会有一个循环,即x的食物是y,而y的食物z是x的天敌,合并时要一起合并
接下来着重[NOI2001]食物链这一道题讲一下如何使用种类并查集
-
首先根据题意可知分为三类,分别是 self(自身)、eat(食物)和enemy(天敌) 。
-
我们假设1~ N为自身,N~ 2N为食物,2N~ 3N为天敌,当然也可以设N2N为自身,2N3N为自身也可以,
当然这都不重要
重要的是:如何把该合并的合并到一块,那么这个范围是什么??(这才是进一步要讨论的)
因为题目中要判断假话,这个其实可以不用太管,题目中只有两种话,一种是X和Y是同类,那么如何判断X和Y是否是同类呢??
-
我们已经设1~N为自身,那么如果X和Y是同类,即X的天敌就是Y的天敌,X的食物就是Y的食物,判断:如果X的食物是Y,或者X的天敌是Y,那么这句话就为假话
-
因为我们已经设N~2N为食物,即,如果我们查询到X+N的祖先和Y的祖先相同,则证明X和Y不是同类,那么代码也就好写很多
代码永远都不是最重要的,最重要的是思考的过程和不看题解的决心
下面代码就是我在上面刚刚分析的,x+n为x的食物,如果(x的食物)和y的祖先相同,则是假话,同理,x的天敌和y的祖先相同也是假话
if(find(x+n)==find(y)) ans++;
if(find(x+n*2)==find(y)) ans++;
第一种话终于解决了,下面我们来看第二种话,X的食物是Y,即X+N的祖先和Y的祖先相同
那么下面两种情况都是背离的
-
X和Y是同一种类,即find(x)==find(y)
-
X的天敌是Y,即X+2N的祖先和Y的祖先相同,符合这两种情况都是假话
我想这个代码恐怕不用我写,根据前两个例子,我想你会很快推出来的
最后我们来考虑如何合并,既然X的食物是Y,设Y的食物是Z,则Z是X的天敌,那么合并就很好写了
merge(x+n,y);//x的食物与y合并
merge(x+n*2,y+n)//x的天敌与y的食物合并
merge(x,y+n*2)//x与y的天敌合并
最后发一下核心代码,希望可以以思考为主,AC代码仅供参考
if(x>n||y>n) ans++;//超出范围,是假话
else if(z==1) {//第一种情况
if(find(x)==find(y+n)||find(x)==find(y+n*2)) ans++;
else {
merge(x,y);
merge(x+n,y+n);
merge(x+2*n,y+2*n);
}
}
else {//第二种情况
if(find(x)==find(y)||find(x)==find(y+n)) ans++;
else {
merge(x,y+n*2);
merge(x+n,y);
merge(x+n*2,y+n);
}
}
下面书上的话送给大家
《算法竞赛进阶指南》
经过不断的思考、分析,最终我们没有用任何高级的算法,仅靠遍历、扫描,就非常简洁、高效地解决了本题。
虽然很多问题的时间复杂度是有下界的,但从某种程度上说,算法的设计、优化是永无止境的。
读者不应被已有的思维束缚,或只满足于得到AC,而是应该尽可能地挖掘、总结一个模型的相关性质,探究其本质。
截至作者完稿,使用搜索引擎都会发现一个有趣的现象,网路上的多数题解均采用了floyed、单调队列等不必要的解法,提出不同思路者甚少,
由此可见,很多算法学习者抄题解而欠思考的现象极其严重,这是每位读者应该避免的行为。