题意:有n个人玩游戏,每个人会说另一个人是狼人或者村民,已知狼人可能说谎话,村民一定说真话,问最终有几个人一定是狼人,几个人一定是村民。
参照题解。村民边反向建边,狼人边正向建边。枚举所有的狼人边,如果这条狼人边的起点和终点同属于一个连通块则标记终点所在子树都是狼人。用并查集维护连通块。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
int T, n, x;
char s[20];
bool ans[maxn], vis[maxn];
int belong[maxn];
struct lEdge { // 狼人边
int from, to;
lEdge() {}
lEdge(int u, int v): from(u), to(v) {}
} ledge[maxn];
int cntlE = 0;
struct Edge { // 村民边
int from, to, next;
} edge[maxn];
int head[maxn], cntE = 0;
int par[maxn];
int find(int x) {
if (x == par[x]) return x;
else return par[x] = find(par[x]);
}
void join(int x, int y) {
x = find(x);
y = find(y);
if (x == y)
return;
par[y] = x;
}
bool same(int x, int y) {
return find(x) == find(y);
}
void init() {
for (int i = 1; i <= n; i++) {
par[i] = i;
}
memset(ans, false, sizeof(ans));
memset(head, -1, sizeof(head));
cntE = 0;
cntlE = 0;
}
void addedge(int u, int v) {
edge[cntE].from = u;
edge[cntE].to = v;
edge[cntE].next = head[u];
head[u] = cntE++;
}
void dfs(int rt) {
vis[rt] = true;
ans[rt] = true;
for (int i = head[rt]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (!vis[v]) {
dfs(v);
}
}
}
int main() {
#ifdef __APPLE__
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
init();
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
scanf("%s", s);
if (s[0] == 'w') {
ledge[cntlE++] = lEdge(i, x);
}
else {
addedge(x, i);
join(x, i);
}
}
memset(vis, false, sizeof(vis));
for (int i = 0; i < cntlE; i++) {
if (same(ledge[i].from, ledge[i].to)
&& !vis[ledge[i].to]) {
dfs(ledge[i].to);
}
}
int sum = 0;
for (int i = 1; i <= n; i++) {
if (ans[i]) {
sum++;
}
}
printf("0 %d\n", sum);
}
}