1594D题解 & 并查集 +DFS

Description

Theofanis started playing the new online game called “Among them”.
However, he always plays with Cypriot players, and they all have the
same name: “Andreas” (the most common name in Cyprus).In each game,
Theofanis plays with n n n other players. Since they all have the same
name, they are numbered from 1 1 1 to n n n.The players write m m m comments
in the chat. A comment has the structure of " i i i j j j c c c" where i i i
and j j j are two distinct integers and c c c is a string ( 1 ≤ i , j ≤ n 1 \le i, j \le n 1i,jn; i ≠ j i \neq j i=j; c c c is either imposter or crewmate). The comment means
that player i i i said that player j j j has the role c c c.An imposter
always lies, and a crewmate always tells the truth. Help Theofanis
find the maximum possible number of imposters among all the other
Cypriot players, or determine that the comments contradict each other
(see the notes for further explanation).Note that each player has
exactly one role: either imposter or crewmate.

Input

The first line contains a single integer t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104) — the number of test cases. Description of each test case follows.The first line of each test case contains two integers n n n and m m m ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1n2105; 0 ≤ m ≤ 5 ⋅ 1 0 5 0 \le m \le 5 \cdot 10^5 0m5105) — the number of players except Theofanis and the number of comments.Each of the next m m m lines contains a comment made by the players of the structure " i i i j j j c c c" where i i i and j j j are two distinct integers and c c c is a string ( 1 ≤ i , j ≤ n 1 \le i, j \le n 1i,jn; i ≠ j i \neq j i=j; c c c is either imposter or crewmate).There can be multiple comments for the same pair of ( i , j ) (i, j) (i,j).It is guaranteed that the sum of all n n n does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105 and the sum of all m m m does not exceed 5 ⋅ 1 0 5 5 \cdot 10^5 5105.

Output

For each test case, print one integer — the maximum possible number of imposters. If the comments contradict each other, print − 1 -1 1.

1 建图

利用二分图的思想,把一个命题拆成 对 和 错 两个点,即 u 点表示 u 为真,u+n 点表示 u 为假。
显然,如果出现一条 u -> u+n 的边,则产生矛盾,这可以直接使用并查集判断。

根据题意,如果 u 指定 v 为 imposter ,则若 u 为 imposter ,v为 crewmate ,反之亦然。
在这里插入图片描述

    while (m--)
    {
        cin >> u >> v >> s;
        if (s == "imposter")
        {
        	//无向图
            g[u].push_back(v + n);
            g[v + n].push_back(u);
            g[v].push_back(u + n);
            g[u + n].push_back(v);
			
			//并查集判断逻辑链上是否冲突
            Union(u, v + n);
            Union(v, u + n);
        }
        else
        {
            g[u].push_back(v);
            g[v].push_back(u);
            g[v + n].push_back(u + n);
            g[u + n].push_back(v + n);

            Union(u, v);
            Union(u + n, v + n);
        }
    }

2 DFS

在保证每一条逻辑链不发生冲突之后。我们可以将图看成一些逻辑链的集合。这些链将是独立并且逻辑自洽的。

我们遍历每一条链,记录链上的 imposter 数量 cnt 和链的深度 res,则有两种情况。如果这条链是真的(即链上的 imposter 确实是 imposter) ,则这条链上的 imposter 数量为 cnt,如果这条链是假的,则这条链上的 imposter 数量为 res - cnt 。对每条链取它们的最大值,相加得到答案。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef pair<int, int> pii;
#define all(x) x.begin(), x.end()
#define maxi(x) max_element(x.begin(), x.end()) - x.begin()
#define mini(x) min_element(x.begin(), x.end()) - x.begin()
#define cmax(x, y) x = max(x, y)
#define cmin(x, y) x = min(x, y)

const int maxn = 5e5 + 10;
vector<vector<int>> g;
int idx, f[maxn], cnt;
bool st[maxn];
int n, m, u, v;
string s;

int find(int x)
{
    if (x == f[x])
        return x;
    return f[x] = find(f[x]);
}

void Union(int a, int b)
{
    a = find(a);
    b = find(b);
    if (a != b)
        f[a] = b;
}

int dfs(int u, int fa)
{
    if (st[u])
        return 0;
    st[u] = 1;
    if (u > n)
        cnt++;
    int res = 1;
    for (auto v : g[u])
    {
        if (v == fa)
            continue;
        res += dfs(v, u);
    }
    return res;
}

void init()
{
    g.clear();
    g.resize(2 * n + 1);
    for (int i = 1; i <= 2 * n + 1; i++)
        f[i] = i, st[i] = 0;
}

void solve()
{

    cin >> n >> m;
    init();

    while (m--)
    {
        cin >> u >> v >> s;
        if (s == "imposter")
        {
            g[u].push_back(v + n);
            g[v + n].push_back(u);
            g[v].push_back(u + n);
            g[u + n].push_back(v);

            Union(u, v + n);
            Union(v, u + n);
        }
        else
        {
            g[u].push_back(v);
            g[v].push_back(u);
            g[v + n].push_back(u + n);
            g[u + n].push_back(v + n);

            Union(u, v);
            Union(u + n, v + n);
        }
    }

    for (int i = 1; i <= n; i++)
        if (find(i) == find(i + n))
        {
            cout << -1 << endl;
            return;
        }

    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        cnt = 0;
        if (!st[i] && !st[i + n])
        {
            int x = dfs(i, -1);
            ans += max(cnt, x - cnt);
        }
    }
    cout << ans << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值