本文已参与「新人创作礼」活动,一起开启掘金创作之路。
题目链接:
codeforces.com/problemset/…
有 n 个人,每个人可能是诚实的人或者说谎的人。
诚实的人永远说真话,说谎的人永远说假话。
有 m 个条件,分别表示 i 说 j 是诚实的人/说谎的人。
你需要判断在这些限制条件下,最多有可能有几个诚实的人,或输出 -1 声明不可能存在这种情况。
分析规则:
- 若A说B诚实,则AB要么都诚实,要么都是骗子
- 若A说B骗子,则A诚实B骗子 ,或者A骗子B诚实
可以发现当一个人诚实和骗子确定下来,与他相关的人都会确定下来。
考虑用一个并查集进行维护,并查集开两倍的空间。
用x表示表示一个人,则x表示他诚实,x + n表示他是骗子。所以并查集前半部分的空间开的是诚实的人的,后半部分开的是骗子的人的。
注意: 初始化时,只有前半部分的人的sz数组初始化为1,其余都为0
如果,则会产生冲突,结果为-1
并查集中因为一正一反,如果有两种情况,那么这两种情况是相反的。用个图来理解
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int N = 1e5 + 5;
struct dsu
{
vector<int> f, sz;
dsu(int n)
{
f.resize(n);
sz.resize(n, 1);
for(int i = 1; i < n; i++) f[i] = i;
}
int find(int x)
{
return x == f[x] ? x : f[x] = find(f[x]);
}
void merge(int x, int y)
{
x = find(x);
y = find(y);
if(x == y) return;
if(sz[x] < sz[y]) swap(x, y);
f[y] = x;
sz[x] += sz[y];
sz[y] = 0;
}
};
void solve()
{
int n, m;
cin >> n >> m;
dsu tr(2 * n + 1);
for(int i = n + 1; i <= 2 * n; i++)
tr.sz[i] = 0;
for(int i =1; i <= m; i++)
{
int x, y;
string s;
cin >> x >> y >> s;
if(s[0] == 'c') tr.merge(x, y), tr.merge(x + n, y + n);
else tr.merge(x, y + n), tr.merge(x + n, y);
}
int res = 0;
for(int i = 1; i <= n; i++)
{
int x = tr.find(i), y = tr.find(i + n);
if(x == y)
{
cout << -1 << "\n";
return;
}
res += max(tr.sz[x], tr.sz[y]);
tr.sz[x] = 0, tr.sz[y] = 0;
}
cout << res << "\n";
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while(t--) solve();
return 0;
}