题意:村民只会说真话,狼可能说假话. (i,j,k)表示第i个人说第j个人是k(k=村民或者狼)[i=1..n].i!=j.
n<=1e5.总共有2^n种情景(有些可能非法.). 问有多少人一定为村民,以及有多少人一定为狼?
因为狼可以将真话也可以讲假话. 假如n个人全部为狼,不会产生任何矛盾,
第i人说其他人为村民就当做假话,说别人为狼就当做真话.所以一定为村民的人数为0.
先不考虑狼边(第i个人说第j个人为狼.),则原图分成若干个联通分量.
因为每个点只有一个出边, 连通分量有两种,n点n条边的基环树, n点n-1条边的树.
基环树的人为村民不会有任何矛盾.
对于第二种联通分量,发现缺边的是树根.
若连的是其他联通分量,那么这颗树显然都可以当村民.
否则连的是自己的某个子孙.
此时若根为狼,那么整个树都为狼,
若根为村民,那么根的出边值值向x时,村民只说真话,x为狼,那么子树x说x为村民为谎话,子树x都为狼.
所以无论怎样.子树x一定为狼.
因为只要求肯定为狼的个数,那么反向建树,求出每个子树的大小,以及狼边是否连的是自己的子树.
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int T,n,x,a[N],fa[N],sz[N],in[N];
vector<int> e[N];
void dfs(int u,int rt){
sz[u]=1;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
fa[v]=rt;
dfs(v,rt);
sz[u]+=sz[v];
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;i++) e[i].clear(),fa[i]=i,in[i]=0,a[i]=0;
string s;
for(int i=1;i<=n;i++){
cin>>x>>s;
if(s=="werewolf") a[i]=x;
else{
//(i,x) .in[u]==0 -> u真正出边为狼边. u为根.
in[i]++;
e[x].push_back(i);
}
}
for(int i=1;i<=n;i++)
if(!in[i]) dfs(i,i);
int res=0;
for(int i=1;i<=n;i++){
if(in[i]|| fa[a[i]]!=i) continue;
res+=sz[a[i]];
}
cout<<0<<' '<<res<<'\n';
}
return 0;
}