题目链接: The Number of Imposters
大致题意
有 n n n个人, m m m个关系.
关系以a b c
的形式给出, 当
c
=
=
0
c==0
c==0时, 表示
a
a
a说
b
b
b是好人, 反之表示
a
a
a说
b
b
b是坏人.
好人只会说真话, 坏人只会说假话. 每个人只能是好人或坏人中的一种.
问: 这 m m m对关系中是否存在矛盾, 如果有矛盾则输出"-1", 反之输出最大的坏人数量.
解题思路
并查集 (读完题一看, 这不是典中典?)
首先我们考虑 c c c的不同取值下的情况:
对于a b 0
的形式, 表示
a
a
a说
b
b
b是好人, 那么此时
a
a
a和
b
b
b的关系一定是相同的, 要么都是好人, 要么都是坏人.
对于a b 1
的形式, 表示
a
a
a说
b
b
b是坏人, 那么此时
a
a
a和
b
b
b的关系一定是相反的, 一定是一好一坏.
对于每对关系, 我们可以用扩展域并查集 或 带权并查集来维护.
当某个点与根结点的距离为偶数时, 表示这个点与根结点类型相同(同好/同坏).
当某个点与根结点的距离为奇数时, 表示这个点与根结点类型相反(一好一坏).
对于每个根结点, 我们分别维护与当前节点类型相同和类型不同的点的数量(相同记为 c o u 0 cou0 cou0, 不同记为 c o u 1 cou1 cou1).
最终答案就是每个连通集合中的 m a x ( c o u 0 , c o u 1 ) max(cou0, cou1) max(cou0,cou1)之和.
对于有矛盾的情况, 如果两个点已经在同一集合中, 且两点的关系不同于当前给定的关系 c c c, 则矛盾.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 2E5 + 10;
int p[N], dis[N]; int cou[N][2];
int find(int x) { //带权并查集
if (x != p[x]) {
int root = find(p[x]);
dis[x] ^= dis[p[x]];
p[x] = root;
}
return p[x];
}
int main()
{
int t; cin >> t;
while (t--) {
int n, m; scanf("%d %d", &n, &m);
rep(i, n) p[i] = i, dis[i] = 0, cou[i][1] = 0, cou[i][0] = 1;
bool flag = 1;
rep(i, m) {
int a, b; char c[10]; scanf("%d %d %s", &a, &b, c);
bool val = c[0] == 'i' ? 1 : 0;
int pa = find(a), pb = find(b);
if (pa == pb) {
if ((dis[a] ^ dis[b]) != val) flag = 0;
}
else {
p[pb] = pa;
dis[pb] = val ^ dis[a] ^ dis[b]; //手推一下公式即可得出
cou[pa][1] += cou[pb][dis[pb] ^ 1];
cou[pa][0] += cou[pb][dis[pb]];
}
}
if (!flag) { puts("-1"); continue; }
int res = 0;
rep(i, n) if (find(i) == i) res += max(cou[i][0], cou[i][1]);
printf("%d\n", res);
}
return 0;
}