解题思路
唉一开始理解错题意,打了一个裸的并查集。。。我太菜了啊啊啊啊啊
巨爷们说这道题好像有两种方法:
算法一:扩展域并查集
引入一个新的并查集拓展:扩展域并查集。
这种做法用到了图论中的经典思想:拆点,即当图中每一个结点需要表示多个信息时,将每个节点拆成多个点,拆出的每个点分别表示原点的一部分信息。
回到本题,我们考虑将动物按吃与被吃的关系分类:以 x x x动物为例,引入虚拟动物 x + n x+n x+n表示 x x x吃的动物,引入虚拟动物 x + 2 ∗ n x+2*n x+2∗n表示被 x x x吃的动物,将同种动物放到同一个集合中,可以用并查集维护。
合并并查集的操作方法如下:
当给出的x和y同类时,合并
x
x
x和
y
y
y所在的集合,合并
x
+
n
x+n
x+n和
y
+
n
y+n
y+n所在的集合,合并
x
+
2
∗
n
x+2*n
x+2∗n和
y
+
2
∗
n
y+2*n
y+2∗n所在的集合.
判断假话:以给出的话表示 x x x和 y y y是同类为例,若之前得出 x x x和 y + n y+n y+n(即 x x x和吃 y y y的动物是同类)或, x x x和 y + 2 ∗ n y+2*n y+2∗n(即x和被y吃的动物是同类)显然都可以判定为假话。 x x x吃 y y y的情况同理。
这种算法的优点很显然降低了思维难度和代码难度,但其付出的代价是更多的空间和更大的时间复杂度常数,运用范围没有带权并查集广泛。
算法二:带权并查集
弄一个很复杂的边权之类的东西,然后还要取模,套一堆式子。所以,由于我太懒了 哈哈 就没有打这种方法。(但我找了一篇非常好的博客,弥补我的罪行:解法二
PS:注意要先特判掉 x x x或 y y y大于 n n n和 x x x等于 y y y的情况
代码
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<bitset>
using namespace std;
int n,k,fa[150010],w,x,y,ans;
int find(int x){
if(fa[x]!=x)
return fa[x]=find(fa[x]);
else return fa[x];
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=3*n;i++)//i+n表示天敌,i+2*n表示猎物
fa[i]=i;
while(k--)
{
scanf("%d%d%d",&w,&x,&y);
if(x>n||y>n)
{
ans++;
continue;
}
int xx=find(x),yy=find(y),sx=find(x+n),sy=find(y+n),tx=find(x+2*n),ty=find(y+2*n);
//xx,yy表示x和y的同类,sx,sy表示x和y的天敌,tx,ty表示x和y的猎物
if(w==1)
{
if(xx==sy||xx==ty){//说是同类,却是天敌或猎物,就是假
ans++;
continue;
}
else fa[xx]=yy,fa[sx]=sy,fa[tx]=ty;
}
if(w==2)
{
if(xx==yy||sx==yy)//说是x吃y,却是同类或y吃x,就是假
{
ans++;
continue;
}
else fa[xx]=sy,fa[sx]=ty,fa[tx]=yy;//x的同类是y的天敌,x的天敌是y的猎物,x的猎物是y的同类
}
}
printf("%d",ans);
}