1. 程式人生 > >HDU 6370 Werewolf 思維,基環樹.

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;
}