【HDU 6370】暑期多校day6 Werewolf (推理 基环树)

转自:https://blog.csdn.net/qq_33330876/article/details/81530546

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6370

题意:有 n 个人在玩只有狼人和村民两种身份的狼人杀,他们在互相指认,形式为“ x 是狼人/村民”。限制:1)每个人都不能指认自己,2)村民必须说真话,3)狼人可能说谎。对于每组测试数据,要求输出一定为村民的玩家数目,一定为狼人的玩家数目。

分析:
根据题意可知,村民只能说实话,而狼人既可以说实话又可以说谎,所以任何一个说实话的村民被替换为狼人,都会新出现一种可行解。因此在所有情况下,“一定为村民的玩家”数目必为0。那么问题就转化为:求”一定为狼人的玩家”数目,既求”一定会说谎的玩家”数目,也就是求”一定不是说实话”的玩家数目。

接下来分析:为什么有些玩家“一定不是说实话”。出现这种情况的原因一定是:如果某玩家说的是真话,那么就会出现自相矛盾的情况——形如: a 指证 b 玩家是村民,b 玩家指证 c 玩家是村民,而 c 玩家指证 b 玩家是狼人,那么 a、b 就必然是狼人,因为他们的指证与产生的间接指证产生了矛盾。既如果某个人被“他直接或间接指证为村民的人”指证是狼人,那么他本人,以及所有“直接或间接指证他为村民的人”都一定没有说实话。

由题目性质可知,每个玩家只能指认一个其他玩家。所以如果以一个“指认他人是狼人”的玩家 x 为根,可以构造出一个由“直接指认或间接指认 x 玩家为村民”的玩家构成的树,结点间以指认关系连边。那么如果某个根指认的狼人 y 是它的后代结点,以这个“被指认为狼人的玩家的结点 y ”为根的子树上的所有玩家就一定没有说实话。因为这颗子树上的玩家,都同时指证或间接指证“x 和 y 都是村民”,而 x 认为 y 是狼人,所以都会自相矛盾。

代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn=int(1e5)+111;
int n, ans=0;
int pt[maxn];
vector<int> to[maxn];

void init() {
    register int i;
    for(ans=0,i=1;i<=n;++i) {
        pt[i]=false;
        to[i].clear();
    }
    return;
}

void dfs(int pt_cur,int k,bool flag) {
    if(pt_cur==k) flag=true;
    ans+=flag;
    for(int i=0;i<(int)to[k].size();++i)
        dfs(pt_cur,to[k][i],flag);//往上搜索
    return;
}

void work() {
    scanf("%d",&n);
    init();
    register int i,x;
    char s[11];
    for(i=1;i<=n;++i) {
        scanf("%d%s",&x,s);
        if(s[0]=='w') pt[i]=x;//储存狼人
        else to[x].push_back(i);//反向建边,建一条从被指定为村民的人 到投票人的一条边
    }
    for(i=1;i<=n;++i) if(pt[i])
        dfs(pt[i],i,0);//pt[i]为被指定的狼  i是投票人
    printf("%d %d\n",0,ans);
    return;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    freopen("output.txt","w",stdout);
#endif
    int T;
    for(scanf("%d",&T);T;T--)
        work();

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36300700/article/details/81538105