HDU 6370 Werewolf

题目:HDU 6370 Werewolf

题意:
一群人玩狼杀,但是只有两种身份,村和狼,然后分别指认别人的身份
村一定说真话,而狼可以说真话说假话,最后问有几个人一定是村,有几个人一定是铁狼

思路:
先是铁村的个数,假设所有人都是狼,显然这个情况是合法的,所以铁村的个数是0,重点就是铁狼的个数
把评价当成边,先忽略狼边,建立一条有向的村边,用村边建图就会有很多个联通块,
这些联通块要么是基环树(点数=边数)要么是树(点数=边数+1)
对于基环树,假设这个联通块所有的人都是村,那么很显然这个方案是合法的,也就是说这些人不是铁狼

再说树,每个人都会有一句评价,那么之所以会是树,就是因为这个联通块存在某个点是狼边的端点
若是这条狼边是描述的狼是另一个联通块的,那么也存在一种方案为当前树节点是村民,其他人都是狼。
因此树上的节点不是铁狼。

最后的就是狼边描述的是这颗树上面的节点,举个例子
1说2是村,2说3是村,3说4是村,而4说2是狼。
因为4说别人是狼,所以先假设4是狼,那么3说4为村,说谎的3也就是狼,类推下去就123都是狼
那么假设4是村,那么2和1就都是狼
综上所述可以得到的结论就是狼边的两端点在同一个联通块的话,被指为狼的点以及被他波及到的评价者都是狼

做法:并查集+建图深搜
并查集维护村边联通块,然后有向村边是被评价者指向评价者,vector保存狼边
直接枚举狼边,看两端点是否为同一联通块,是的话被评价为狼的点以及它指向的所有节点都是铁狼,深搜计数即可

AC代码

#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
using namespace std;
typedef pair<int, int> P;
const int N = 1e5 + 10;

int n, T, ans, u;
char s[20];

vector<int> g[N];
vector<P> v;
int pre[N];

int Find(int x)
{
    if(pre[x] + 1)  return pre[x] = Find(pre[x]);
    return x;
}

void Mix(int x, int y)
{
    x = Find(x), y = Find(y);
    if(x != y)  pre[x] = y;
}

void dfs(int u)
{
    ans ++;
    for(int i = 0; i < g[u].size(); i ++)   dfs(g[u][i]);
}

int main()
{
//  freopen("1.txt", "r", stdin);
    for(scanf("%d", &T); T; T --)
    {
        memset(pre, -1, sizeof pre);
        v.clear();
        scanf("%d", &n);
        for(int i = 0; i <= n; i ++)    g[i].clear();
        for(int i = 1; i <= n; i ++)
        {
            scanf("%d%s", &u, s);
            if(s[0] == 'w') v.pb( P(i, u) );
            else     g[u].pb (i), Mix(i, u);
        }

        ans = 0;
        for(int i = 0; i < v.size(); i ++)
        {
            int x = v[i].fi, y = v[i].se;
            if(Find(x) == Find(y))  dfs(y);
        }

        printf("0 %d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_38287798/article/details/81564894