题目链接
题面
题目描述
动物王国中有三类动物
A
,
B
,
C
A,B,C
A,B,C,这三类动物的食物链构成了有趣的环形。
A
A
A 吃
B
B
B,
B
B
B 吃
C
C
C,
C
C
C 吃
A
A
A。
现有
N
N
N 个动物,以
1
∼
N
1 \sim N
1∼N 编号。每个动物都是
A
,
B
,
C
A,B,C
A,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这
N
N
N 个动物所构成的食物链关系进行描述:
- 第一种说法是
1 X Y
,表示 X X X 和 Y Y Y 是同类。 - 第二种说法是
2 X Y
,表示 X X X 吃 Y Y Y。
此人对 N N N 个动物,用上述两种说法,一句接一句地说出 K K K 句话,这 K K K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
- 当前的话与前面的某些真的话冲突,就是假话;
- 当前的话中 X X X 或 Y Y Y 比 N N N 大,就是假话;
- 当前的话表示 X X X 吃 X X X,就是假话。
你的任务是根据给定的 N N N 和 K K K 句话,输出假话的总数。
输入格式
第一行两个整数,
N
,
K
N,K
N,K,表示有
N
N
N 个动物,
K
K
K 句话。
第二行开始每行一句话(按照题目要求,见样例)
输出格式
一行,一个整数,表示假话的总数。
样例 #1
样例输入 #1
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
样例输出 #1
3
提示
对于全部数据, 1 ≤ N ≤ 5 × 1 0 4 1\le N\le 5 \times 10^4 1≤N≤5×104, 1 ≤ K ≤ 1 0 5 1\le K \le 10^5 1≤K≤105。
题意
有
n
n
n 只动物,动物们分为三种类型
(
A
,
B
,
C
)
(A,B,C)
(A,B,C),其中有
A
A
A吃
B
B
B,
B
B
B吃
C
C
C,
C
C
C吃
A
A
A神奇关系(和石头剪刀布相类似)
现给出
m
m
m条语句,其格式为
P
P
P
x
x
x
y
y
y
- P P P为 1 1 1时,表示 x x x是 y y y的同类
- P P P为 2 2 2时,表示 x x x吃 y y y
现求有多少条语句是谎言,谎言的定义如下:
- x > n x>n x>n或 y > n y>n y>n
- x x x吃 y y y时, x = = y x==y x==y(即 x x x自己吃自己)
- 与前面的语句相矛盾
当确定当前语句为谎言时,不执行当前语句
反之,执行当前语句
思路
这题其实是团伙的升级版,用的算法也是拓展域,只不过是由两个域变成了三个域
由于
A
A
A吃
B
B
B,
B
B
B吃
C
C
C,
C
C
C吃
A
A
A的“环状”关系,我们可以将动物们的关系分三大类(同类、猎物、敌人)
分别表示为(假设当前的动物编号为
i
i
i)
f a [ i ] fa[i] fa[i] → \to → 同类
f a [ i + n ] fa[i+n] fa[i+n] → \to → 猎物
f a [ i + 2 n ] fa[i+2n] fa[i+2n] → \to → 天敌
然后我们就可以用类似团伙的做法进行并查集连接(对动物们指定“父亲”)
通过
f
i
n
d
find
find数组就可以判断是否是同一个祖先,然后就可以进而判断关系的正确性
接下来我们来仔细分析一下如何去判断语句的正确性以及如何处理语句
x > n x>n x>n或 y > n y>n y>n 无聊判断输入越界
若 x x x是 y y y的同类 ( P = = 1 ) (P==1) (P==1)(假设状态)如果 x x x是 y y y的猎物 或 x x x是 y y y的天敌 成立,这就会推翻 x x x是 y y y的同类 这个假设,判断为谎言
否则就证实了 x x x是 y y y的同类 这个假设,接下来就可以进行关系的连接了
因为 x x x是 y y y的同类,所以 x x x的猎物 自然也就是 y y y的猎物、 x x x的天敌 也就是 y y y的天敌若 x x x吃 y y y ( P = = 2 ) (P==2) (P==2)
如果 x = = y x==y x==y(即自己吃自己)成立,判断为谎言
如果 x x x是 y y y的同类 或 x x x是 y y y的猎物(等价于 x x x的天敌是 y y y)成立,这就会推翻 x x x吃 y y y 这个假设,判断为谎言
否则就证实了 x x x吃 y y y 这个假设,接下来就可以进行关系的连接了
因为 x x x吃 y y y,所以 x x x的猎物是 y y y、 x x x是 y y y的天敌、 x x x的天敌是 y y y的猎物
为什么
x
x
x的天敌是
y
y
y的猎物呢?
其实这个细节我一开始也没想到,其实只要在图纸上画一下就明白了
假设
x
x
x为
A
A
A类动物、
y
y
y为
B
B
B类动物、
z
z
z为
C
C
C类动物
即
x
x
x吃
y
y
y,
y
y
y吃
z
z
z,
z
z
z吃
x
x
x
我们可以从图中提取出以下信息:
- y y y的猎物是 z z z
- x x x的天敌是 z z z
所以
y
y
y的猎物是
x
x
x的天敌(
x
x
x的天敌是
y
y
y的猎物)
代码实现细节看代码注释
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k,orz,x,y,ans,fa[50010*3];
//fa[1~n]为同类域
//fa[n+1~2n]为猎物域
//fa[2n+1~3n]为天敌域
int find(int x){//并查集 find"父亲"
if(fa[x]!=x)fa[x]=find(fa[x]);
return fa[x];
}
void merge(int x,int y){//并查集连接
fa[find(x)]=find(y);
return;
}
signed main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=3*n;i++)fa[i]=i;//初始化
while(k--){
scanf("%lld%lld%lld",&orz,&x,&y);
if(x>n||y>n)ans++;//x和y的越界特判
else if(orz==1){
//假设x是y的同类
if(find(x)==find(y+n)||find(x)==find(y+2*n))ans++;
//如果x是y的猎物或x是y的天敌在前面的语句出现过,则判断为谎话
else{
//x是y的同类成立
merge(x,y);//x的同类是y
merge(x+n,y+n);//x的猎物是y的猎物
merge(x+2*n,y+2*n);//x的天敌是y的天敌
}
}else{
//假设x吃y
if(x==y||find(x)==find(y)||find(x)==find(y+n))ans++;
//如果x吃y,又x==y,则是x自己吃自己,判断为谎话
//如果x是y的同类 或 x是y的猎物,则与x吃y(x是y的天敌)相矛盾,判断为谎话
else{
//x吃y成立
merge(x+n,y);//x的猎物是y
merge(x+2*n,y+n);//x的天敌是y的猎物(重点,思路里有证明)
merge(x,y+2*n);//x是y的天敌
}
}
}
printf("%lld",ans);
return 0;
}