Werewolf (并查集)

                                                 Werewolf   HDU - 6370 

"The Werewolves" is a popular card game among young people.In the basic game, there are 2 different groups: the werewolves and the villagers. 

Each player will debate a player they think is a werewolf or not. 

Their words are like "Player x is a werewolf." or "Player x is a villager.". 

What we know is : 

1. Villager won't lie. 

2. Werewolf may lie. 

Of cause we only consider those situations which obey the two rules above. 

It is guaranteed that input data exist at least one situation which obey the two rules above. 

Now we can judge every player into 3 types : 

1. A player which can only be villager among all situations, 

2. A player which can only be werewolf among all situations. 

3. A player which can be villager among some situations, while can be werewolf in others situations. 

You just need to print out the number of type-1 players and the number of type-2 players. 

No player will talk about himself.

Input

The first line of the input gives the number of test cases T.Then T test cases follow. 

The first line of each test case contains an integer N,indicating the number of players. 

Then follows N lines,i-th line contains an integer x and a string S,indicating the i-th players tell you,"Player x is a S." 

limits: 

1≤T≤10 

1≤N≤100,000

1≤x≤N

S∈ {"villager"."werewolf"}

Sample Input

1
2
2 werewolf
1 werewolf

Sample Output

0 0

题意:狼人杀游戏中,农民只能说真话,狼人可以说真话,也可以说假话,每个人给一个描述,描述另一个人是农民还是狼人,描绘的人不能是本人,问一定是农民的或一定是狼人的有多少人。

思路:因为说真话的不一定都是农民,全部都是狼人的时候也一定成立,因为狼人真话假话都可以,所以没有人一定是农民。接下来判断谁一定是狼人。

给一个场上出的样例,1说2是农民,2说1是狼人。这样的话,可以判断出1一定是狼人,因为若1是农民,讲真话,2也是农民,讲真话,1是狼人,矛盾了。

再者说,1说2是农民,2说3是农民,3说4是农民,4说1是狼人,那么1一定是狼人,证明同上。

于是我们可以给出一个推广,如果你被一个人认定是狼人,并且这个人还是你认定的农民,或者是被你间接认定的农民,那么,你一定是狼

再者,如果判断出我是狼人,那么说我是农民的人也一定是狼人。

因此,到这里这个题就可以写了。

我们使用并查集来写,刚开始我们只连接进去农民边,记录出来狼人边,最后加进去狼人边,如果狼人边的两端在同一个并查集内,则会产生狼人,首先被认定的人是狼人,其次,指向我的人是狼人,代码中v数组记录是都有谁指向我是农民,fa[]记录的是我指向的下一个人,Q用来寻找指向我是农民的人。如果狼人边的两端在不同的并查集,可以忽略不管,为什么呢,因为咱们假如x认定y是狼人,但是y在另一个并查集中,此时咱们想如果只有y是狼人,此时x整个并查集中时农民,y并查集中有狼人,这样式成立的,但是如果x并查集中是狼人,y并查集中时农民,答案也是成立的,因此这种情况下,没有一定的狼人

代码如下:

#include <cstdio>
#include <iostream>
#include <string.h>
#include <string>
#include <map>
#include <queue>
#include <deque>
#include <vector>
#include <set>
#include <algorithm>
#include <math.h>
#include <cmath>
#include <stack>
 
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
const int maxn=100005,inf=0x3f3f3f3f;
const double pi = acos(-1.0);
 
int t,n,fa[maxn];
int ans;
vector <int> V, v[maxn];
queue<int> Q;
 
void init()
{
    ans = 0;
    V.clear();
    for(int i = 1 ; i <= n ; i++)
    {
        fa[i] = i;
        v[i].clear();
    }
    while(!Q.empty())
        Q.pop();
}
 
int Find(int x)
{
    if(x==fa[x])
        return x;
    return fa[x] = Find(fa[x]);
}
 
void un(int u,int v)
{
    int fu,fv;
    fu = Find(u);
    fv = Find(v);
    if(fu != fv)
        fa[fu] = fv;
}
 
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        init();
        int tmp;
        char s[20];
        for(int i = 1; i <=n ;i++)
        {
            scanf("%d",&tmp);
            scanf("%s",s);
            if(s[0]=='v')
            {
                un(i,tmp);//建立农民边
                v[tmp].push_back(i);//说tmp是农民的人
            }
            else //i说tmp是狼 建立狼人边 两两一条边
            {
                V.push_back(i);
                V.push_back(tmp);
            }
        }
        for(int i = 0 ; i <= V.size() -1 ;i++) // 狼人集
        {
            int a,b;
            a = V[i]; // a说b是狼
            b = V[++i];
            if(Find(a)==Find(b)) //狼人边在同一个并查集  那么b一定是狼人
            {
                Q.push(b); // 通过Q寻找说我是农民的狼人
                while(!Q.empty())
                {
                    int f = Q.front();
                    Q.pop();
                    ans++;
                    for(int j = 0 ; j < v[f].size();j++)
                        Q.push(v[f][j]);
                }
 
            }
        }
        if(n==1)
            printf("0 0\n");
        else
            printf("0 %d\n",ans);
    }
    return 0;
}

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值