题目链接:https://www.acwing.com/problem/content/242/
题目
动物王国中有三类动物
A
A
A,
B
B
B,
C
C
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∼N 1∼N 编号。
每个动物都是 A A A, B B B, C C 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
N
N 和
K
K
K,以一个空格分隔。
以下 K K K 行每行是三个正整数 D D D, X X X, Y Y Y,两数之间用一个空格隔开,其中 D D D 表示说法的种类。
若 D = 1 D=1 D=1,则表示 X X X 和 Y Y Y 是同类。
若 D = 2 D=2 D=2,则表示 X X X 吃 Y Y Y。
输出格式
只有一个整数,表示假话的数目。
数据范围
1
≤
N
≤
50000
,
1≤N≤50000,
1≤N≤50000,
0
≤
K
≤
100000
0≤K≤100000
0≤K≤100000
输入样例:
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
输出样例:
3
思路:因为每个动物都是
X
X
X,
Y
Y
Y,
Z
Z
Z 中的一种
可以利用并查集,维护每个点到根节点的距离
到根节点的距离模
3
3
3 为
1
1
1 表示吃根节点 记为
X
X
X
到根节点的距离模
3
3
3 为
2
2
2 表示吃
X
X
X 记为
Y
Y
Y
到根节点的距离模
3
3
3 为
0
0
0 表示吃
Y
Y
Y ,和根节点是同类 记为
Z
Z
Z
那么两个点( X X X 和 Y Y Y )的关系的两种情况就有规律
情况 1 1 1 X X X 和 Y Y Y 是同类
若
X
X
X 和
Y
Y
Y 是同类
那么有
d
[
x
]
%
3
=
0
,
d
[
y
]
%
3
=
0
d[x]\ \%\ 3 = 0\ ,\ d[y]\ \%\ 3 = 0
d[x] % 3=0 , d[y] % 3=0
也就是
(
d
[
x
]
−
d
[
y
]
)
%
3
=
0
(d[x]-d[y])\ \%\ 3 = 0
(d[x]−d[y]) % 3=0
情况 2 2 2 X X X 吃 Y Y Y
若
X
X
X 吃
Y
Y
Y
那么有
(
d
[
x
]
−
d
[
y
]
)
%
3
=
1
(d[x] - d[y])\ \%\ 3 = 1
(d[x]−d[y]) % 3=1
也就是
(
d
[
x
]
−
d
[
y
]
−
1
)
%
3
=
0
(d[x] - d[y] - 1)\ \%\ 3 = 0
(d[x]−d[y]−1) % 3=0
接下来每输入一句话,判断是否矛盾
第二种矛盾直接判断即可
对于剩下矛盾的处理方法:
先找到
X
X
X 和
Y
Y
Y 的根节点
如果根节点相同,就说明这两个点在一棵树上
那么只要不满足对应的式子,就说明这句话和前面的话矛盾
如果根节点不同,就说明不在一棵树上,相当于这句话前面没人说过,那一定是真话,不过我们要把这两个点合并成一棵树
合并的办法就是直接让
X
X
X 的根节点指向
Y
Y
Y 的根节点
如图所示,此时我们要更新 X X X 的根节点到 Y Y Y 的根节点的距离
以情况
1
1
1 (
X
X
X 和
Y
Y
Y 是同类)来举例
此时应该满足
(
d
[
x
]
+
?
)
%
3
=
d
[
y
]
%
3
(d[x]\ +\ ?)\ \%\ 3 = d[y]\ \%\ 3
(d[x] + ?) % 3=d[y] % 3
即
(
d
[
x
]
+
?
−
d
[
y
]
)
%
3
=
0
(d[x]\ +\ ?\ -\ d[y])\ \%\ 3 = 0
(d[x] + ? − d[y]) % 3=0
那么
?
=
d
[
y
]
−
d
[
x
]
\ ?\ = d[y] - d[x]
? =d[y]−d[x]
也就是说
X
X
X 的根节点到
Y
Y
Y 的根节点的距离为
d
[
y
]
−
d
[
x
]
d[y] - d[x]
d[y]−d[x]
即
d
[
p
x
]
=
d
[
y
]
−
d
[
x
]
d[px] = d[y] - d[x]
d[px]=d[y]−d[x]
情况 2 2 2 同理
AC代码
#include <iostream>
using namespace std;
const int N = 50010;
/*
到根节点的距离模3为1表示吃根节点 记为X
到根节点的距离模3为2表示吃X 记为Y
到根节点的距离模3为0表示吃Y,和根节点是同类 记为Z
x <- y <- z <- x
*/
int n,m,ans;
int p[N],d[N]; // d存的是每个点到根节点的距离
int find(int x)
{
if(p[x] != x)
{
int u = find(p[x]); // 先递归,这样d[p[x]]就能更新成到根节点的距离
d[x] += d[p[x]];
p[x] = u;
}
return p[x];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++) p[i] = i;
while(m--)
{
int t,x,y;
scanf("%d%d%d",&t,&x,&y);
if(x > n || y > n) ans++;
else
{
int px,py;
px = find(x);
py = find(y);
if(t == 1) // X和Y是同类
{
if(px == py && (d[y] - d[x]) % 3) // 在一棵树上
{
// d[px] % 3 = 0,d[py] % 3 = 0
ans++;
}
else if(px != py) // 不在一棵树上 合并两棵树
{
/*
(d[x] + ?) % 3 = d[y] % 3 即 (d[x] + ? - d[y]) % 3 = 0
? = d[y] - d[x]
*/
p[px] = py;
d[px] = d[y] - d[x];
}
}
else // t = 2 // X吃Y
{
if(px == py && (d[x] - d[y] - 1) % 3) // 在一棵树上
{
// (d[x] - d[y]) % 3 = 1 即 (d[x] - d[y] - 1) % 3 = 0
ans++;
}
else if(px != py) // 不在一棵树上 合并两棵树
{
/*
(d[x] + ?) % 3 = d[y] % 3 + 1 即 (d[x] + ? - d[y] - 1) % 3 = 0
? = d[y] - d[x] + 1
*/
p[px] = py;
d[px] = d[y] - d[x] + 1;
}
}
}
}
printf("%d\n",ans);
return 0;
}