第一行输入n,k代表n个动物,k条语句
接下来k行
每一行输入d,x,y
d=1代表xy为同类
d=2代表x吃y
要求输出假话数目
问>0<何为假话
1.x或y大于n
2.现在输入的语句与之前语句矛盾
3.自己吃自己
每输入一个询问,就判断x和y是否都被放入同一集合中(也就是是否已确定关系),如果以确定,判断比较,未确定,添加。
确定关系
rel[i]=0(d=1 1-1)
i与根节点为同类
rel[i]=1(d=2 2-1)
i吃根节点
rel[i]=2
根节点吃i
解释三个公式:
公式1:>0<
–rel[x]=(rel[x]+rel[fa])%3
–由向量即可证明
公式2:>0<
–判断所给语句是否符合xy关系
–rel[x]==((d-1)+rel[y])%3
–d-1代表xy之间的关系
–由公式一易证此公式
公式3:>0<
–在将x所在的集合并入y所在集合时^_^
–rel[fx]=(rel[y]+(d-1)-rel[x]+3)%3
–由公式二易证
^_^
#include<iostream> //带偏移量的并查集,重点是如何路径压缩
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int f[50010];//此节点的父节点
int rel[50010];//此节点与其父节点的距离**rel=0同类,rel=1此节点吃父节点,rel=2此节点被其父节点吃
int find(int x){//路径压缩
if(f[x]==x)
return x;
int fa=f[x];
f[x]=find(f[x]);
rel[x]=(rel[x]+rel[fa])%3;
return f[x];
}
void add(int d,int x,int y){
int fx,fy;
fx=find(x);
fy=find(y);
if(fx==fy)
return;//如果两个节点的最终父节点是一个,则不用进行任何操作
//如果不相等将fx的父节点设为y的父节点
f[fx]=fy;
rel[fx]=(rel[y]+(d-1)-rel[x]+3)%3;//将x所在的集合合并到y的集合
}
bool judge(int d,int x,int y){
int fx,fy;
fx=find(x);
fy=find(y);
if(fx!=fy)
return true;
if(rel[x]==((d-1)+rel[y])%3)
return true;
return false;
}
int main(){
int n,k,ans=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){//初始化
f[i]=i;
rel[i]=0;//此节点与自己为同类
}
for(int i=1,d,x,y;i<=k;i++){
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n||(d==2&&x==y))//如果输入动物不在n范围内或自己吃自己那么一定是假话
ans++;
else if(judge(d,x,y)==false)
ans++;
else
add(d,x,y);
}
cout<<ans<<endl;
return 0;
}