动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。
Input
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
Output
只有一个整数,表示假话的数目。
Sample Input
100 7 1 101 1 2 1 2 2 2 3 2 3 3 1 1 3 2 3 1 1 5 5
Sample Output
3
我们先回顾一下并查集的基本操作
初始化
for(int i=1;i<=n;i++) father[i]=i;//令结点等于i,father[i]也可以等于-1
查找
递推代码
int findfa(int x){
while(x!=father[x]){//不是根节点,继续循环
x=father[x];//获得自己的父节点
}
}
递归代码
int findfa(int x){
if(father[i]==x) return x;
else return findfa(father[x]);
}
合并
void Union(int a,int b){
int faA=findfa(a),int faB=findfa(b);
if(faA!=faB) father[faB]=faA;//一定要父节点相连,不然会丢失结点
}
路径压缩
递推的压缩
int findfa(int x){
int a=x;
while(x!=father[x]){
x=father[x];
}
while(a!=father[a]){
int z=a;
a=father[a];
father[z]=x;
}
return x;
}
递归的压缩
int findfa(int v){
if(v==father[v]) return v;
else{
int F=findfa(father[v]);
father[v]=F;
return F;
}
}
以上是最基本的并查集操作,那我们再来分析一下这道题。
我们首先要知道,题目是让我们求关系是否正确,而不是求两两之间的关系,我们就可以根据有无关系来进行分类。有关系的就放到一个并查集中去,没有的就不用管,再在这个并查集中去验证关系是否正确。
为了关系的简化,我们将并查集中的所有关系进行路径压缩,每个点只与根节点判断关系,这样操作是最省时间的。
为了表达关系,我们开一个r【】数组,用来存p[x]->x的关系,
我们设 r[x]=0, 就是只p[x]与x同类
r[x]=1, p[x]吃掉x
r[x]=2, x吃掉p[x]
现在最难的就是关系的更新
我们假设 p[x]吃x x吃y 那么y和p[x]的关系是什么呢? 很显然,y吃p[x], 食物链的关系 p[x]->x->y->p[x]。
我们现在分开写 p[x]->y=p[x]->x+x->y; 是不是像向量形式(其实我是看的大佬的博文)我们继续验证 r[y]=r[x]+1=2 确实是y吃p[x]
(因为此时x与y的关系已知,y的父节点就是x的父节点p[x]),知道这个后,我们就可进行实现了。
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#define LL long long
#define ULL unsigned long long
#define mod 1000000007
#define INF 0x7ffffff
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=50005;
int p[N],r[N];
int findfa(int x)
{
if(x!=p[x]){
int fa=findfa(p[x]);
r[x]=(r[x]+r[p[x]])%3;//我们可以理解为一个链 p[x]->x->x1->x2 p[x]->x2=p[x]->x+x->x1+x1->x2 每一步都是自己加上父节点的r关系
p[x]=fa;
}
return p[x];
}
int Union(int d,int x,int y)
{
int fax=findfa(x),fay=findfa(y);
if(fax==fay){
if(d-1!=((-r[x]+3+r[y])%3)) return 1;//x与y同父亲(p[x]=p[y]) x->y=x->p[x]+p[x]->y.注意符号,p[x]到x的关系为r[x],x到p[x]的关系为-r[x]
else return 0;
}
p[fay]=fax;
r[fay]=(r[x]+d-1+3-r[y])%3;//这里找rootx->rooy=rootx->x+x->y+y->rooty 注意符号
return 0;
}
int main()
{
int n,q,d;
int cot=0;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) p[i]=i,r[i]=0;//并查集的初始化
int x,y;
while(q--){
scanf("%d%d%d",&d,&x,&y);
if(x>n||y>n||(x==y&&d==2)){cot++;continue;}
if(Union(d,x,y)) cot++;
}
printf("%d\n",cot);
return 0;
}