HDU 6370 Werewolf 思维,基环树.

题意:村民只会说真话,狼可能说假话. (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;
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值