本文已参与「新人创作礼」活动,一起开启掘金创作之路。
种类并查集,我粗略解释一下,就是带了种类的并查集???
好吧,这都不重要,重要的是,种类并查集到底该怎么用。
-
种类并查集一般会分为几个种类,根据题目,有时会分为两类,即同类和敌人,在[NOI2001]食物链 中分为了三类,分别是同类,食物和天敌。
-
种类并查集一般数组要相应开到几倍,初始化时也要循环到几倍。
-
一般根据题意,会有一个循环,即x的食物是y,而y的食物z是x的天敌,合并时要一起合并
接下来着重[NOI2001]食物链这一道题讲一下如何使用种类并查集
-
首先根据题意可知分为三类,分别是 self(自身)、eat(食物)和enemy(天敌) 。
-
我们假设1~ N为自身,N~ 2N为食物,2N~ 3N为天敌,当然也可以设N
2N为自身,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);
}
}