分析:
这题还算比较水的。。。真不知道为什么初三的小朋友被卡了。。。
明明人家初二的小朋友拿这题签到来着。。。。
显然,可以所有人都是狼人,这是一定合法的,所以一定是村民的人数必然为0
因此,只需要知道哪些人一定是狼人,就能得到答案。
我们定义一条全是由“认为xx是村民”的边,所组成的链为一条信任链。不同的信任链可以重合。
这种信任链的特点是:对某个信任链上的点而言,它认为所有它的后继节点都是村民(即A认为B是村民,B认为C是村民,等价于A认为C是村民)。且它的后继一定是一条链(即不可能分叉)。
那么一个人是狼人的条件只有2个:
1、它所在的信任链的最后一个节点认为它是狼人。
2、它的信任链的某个后继节点一定是狼人。
否则,一定存在某种方案,使得这个人为村民。
证明很简单,由于狼人可说谎可不说,我们可以假定当前这个人是村民,那必须说真话的无非就是它的信任链上的后继节点,然而这些点都一定不会矛盾(条件2),所以这个点也不会矛盾。
所以只需要存“认为xx是村民”的边,然后用“认为xx是狼人”来查询,即对每个狼人关系处理一下,判断它是否是:它所指认的人所在的信任链的最靠后的节点。
然后确定出所有满足条件1的人,然后沿着信任链反向走,把所遍历到的点都标记为狼人即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
int x;
int n,ans,cnt;
bool used[MAXN],vis[MAXN];
vector<int> a[MAXN];
char s[MAXN];
int dp[MAXN];//这个点所在的信任链的最靠后的点
pair<int,int> l[MAXN];
queue<int> q;
void dfs(int x,int flag){
dp[x]=flag;
for(int i=0;i<a[x].size();i++){
int v=a[x][i];
dfs(v,flag);
}
}
int main(){
int t;
SF("%d",&t);
while(t--){
SF("%d",&n);
cnt=0;
for(int i=1;i<=n;i++){
a[i].clear();
used[i]=0;
vis[i]=0;
dp[i]=0;
}
for(int i=1;i<=n;i++){
SF("%d",&x);
SF("%s",s);
if(s[0]=='w'){
l[++cnt]=make_pair(i,x);
vis[i]=1;
}
else
a[x].push_back(i);
}
for(int i=1;i<=n;i++)
if(vis[i]==1){
dfs(i,i);
}
for(int i=1;i<=cnt;i++){
int u=l[i].first,v=l[i].second;
if(u==dp[v])
if(used[v]==0){
q.push(v);
used[v]=1;
}
}
ans=0;
while(!q.empty()){
int x=q.front();
q.pop();
ans++;
for(int i=0;i<a[x].size();i++)
if(used[a[x][i]]==0){
used[a[x][i]]=1;
q.push(a[x][i]);
}
}
PF("0 %d\n",ans);
}
}