题意:
给定n个玩家,每个玩家只有两种身份;然后每个玩家说某一个玩家是人还是狼,
如果这个玩家是人,那他只会说真话;否则可能说假话可能说真话;
思路:
开始我们以为没法判断是否有确定的解,猜了0 0 不对,后来发现:
当两个玩家A,B:A说B是人,B说A是狼,那如果A是人的话,B就是狼,而A说B是人,矛盾,那么A不可能是人,即判定了A是狼
由此再推,三个,四个....玩家成环的情况,发现 只有这个环中指认狼的的个数唯一的时候,才能确定被指认的玩家是狼,
这一步骤用拓扑判环的方法,把环外面的链去掉;然后遍历每个环看是不是有符合条件的狼存在;
把所有的狼找出来后,然后找指认这个玩家是人的玩家,那他就是狼,因为他说了假话=-=
把狼加入队列,bfs跑一边找到所有的狼
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <set>
#include <queue>
using namespace std;
typedef pair<int,char> P;
const int maxn = 1e5 + 7;
P a[maxn];
int n, v, ans = 0;
int dig[maxn];
bool vis[maxn];
char s[10];
vector<int> vec[maxn];
queue<int> lang;
void solve(int id) {
vis[id] = 1;
int t = a[id].first;
while(1) {
dig[t]--;
if(dig[t]) return;
vis[t] = 1;
t = a[t].first;
}
}
void work(int id) {
int t = id, u = -1, num = 0, cnt = 0;
while(1) {
if(vis[t]) break;
vis[t] = 1;
num++;
if(a[t].second == 'v') cnt++;
else u = a[t].first;
t = a[t].first;
}
if(cnt == num-1) {
//ans++;
lang.push(u);
}
}
void bfs() {
while(!lang.empty()) {
int t = lang.front(); lang.pop(); ans++;
for(auto i : vec[t]) {
if(a[i].second == 'v') lang.push(i);
}
}
}
int main() {
int T; scanf("%d", &T);
while(T--) {
memset(dig, 0, sizeof dig);
memset(vis, 0, sizeof vis);
ans = 0;
while(!lang.empty()) lang.pop();
scanf("%d", &n);
for(int i = 1; i <= n; ++i) vec[i].clear();
for(int i = 1; i <= n; ++i) {
scanf("%d%s", &v, s);
vec[v].push_back(i);
a[i].first = v; a[i].second = s[0];
dig[v]++;
}
for(int i = 1; i <= n; ++i) {
if(vis[i]) continue;
if(dig[i] == 0) solve(i);
}
for(int i = 1; i <= n; ++i) {
if(!vis[i]) {
work(i);
}
}
bfs();
printf("0 %d\n", ans);
}
return 0;
}
/*
4
4
2 w
1 v
4 v
3 w
3
2 v
3 v
1 w
*/