2018 Multi-University Training Contest 6 Werewolf(Tarjan+染色+基环外向树)

 

Problem Description

"The Werewolves" is a popular card game among young people.In the basic game, there are 2 different groups: the werewolves and the villagers.

Each player will debate a player they think is a werewolf or not.

Their words are like "Player x is a werewolf." or "Player x is a villager.".

What we know is :

1. Villager won't lie.

2. Werewolf may lie.

Of cause we only consider those situations which obey the two rules above.

It is guaranteed that input data exist at least one situation which obey the two rules above.

Now we can judge every player into 3 types :

1. A player which can only be villager among all situations,

2. A player which can only be werewolf among all situations.

3. A player which can be villager among some situations, while can be werewolf in others situations.

You just need to print out the number of type-1 players and the number of type-2 players.

No player will talk about himself.

Input

The first line of the input gives the number of test cases T.Then T test cases follow.

The first line of each test case contains an integer N,indicating the number of players.

Then follows N lines,i-th line contains an integer x and a string S,indicating the i-th players tell you,"Player x is a S."

limits:

1≤T≤10

1≤N≤100,000

1≤x≤N

S∈ {"villager"."werewolf"}

Output

For each test case,print the number of type-1 players and the number of type-2 players in one line, separated by white space.

Sample Input

1 2 2 werewolf 1 werewolf

Sample Output

0 0

菜鸡题解:

首先由于所有人都是werewolf的情况是必定合法的,所以必定是villager的人不存在的。进而问题转化为求必定是werewolf的人有多少。然后在纸上瞎画画,可以发现这样一个样例 A说B为villager,B说A为werewolf。显然这样的话A必为werewolf。然后再瞎推推发现这个样例可以扩展为 A说B为villager,B说C为villager,C说D为......N-1说N为villager,N说A为werewolf,结果还是A必为werewolf。然后再瞎想想发现A为werewolf的话所有直接或间接指定A为villager的人都是werewolf。

将以上思路整理一下之后可以发现可以这样做:

先把所有A说B为villager的情况建成A->B的有向边,从而得到一张图V(可能是多个不连通的图),并再建一张由B->A组成的反向图V`。另外我们发现如果图V中有环的话环上的点必定是不确定的,所以跑一遍Tarjan把环点都删掉。然后只需把每条werewolf边加入图中,如果werewolf边A->B连接的两个点在同一个图中(可以用染色法预处理来判断是否同一图)则B必为werewolf,然后通过反向图V`来找到有多少个点直接或间接指定B为villager(记得算上B点自己)然后求和就是结果。

个人菜鸡代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <iostream>

using namespace std;

const int MAXN = 100005;

struct EdgeW{//存werewolf边 
	int from,to;
	EdgeW(int a,int b):from(a),to(b){}
};

vector<struct EdgeW> W;

struct Edge{//存villager正向边 
	int to,_next;
}E[MAXN];

struct EdgeR{//存villager反向边 
	int to,_next;
}ER[MAXN];

int head[MAXN],tot;
int headr[MAXN],totr;

inline void Add(int from,int to){
	E[++tot]._next = head[from];
	E[tot].to = to;
	head[from] = tot;
	ER[++totr]._next = headr[to];
	ER[totr].to = from;
	headr[to] = totr;
}

bool is_instack[MAXN];
int sstack[MAXN],top;
int DFN[MAXN];
int LOW[MAXN];
int timee;
int Belong[MAXN];
int N,cnt;
bool Del[MAXN];//记录强联通分量是否删除 

void Tarjan(int x){
    DFN[x] = LOW[x] = ++timee;
    is_instack[x] = true;
    sstack[++top] = x;
    for(int i=head[x] ; i ; i=E[i]._next){
        if(!DFN[E[i].to]){
            Tarjan(E[i].to);
            if(LOW[E[i].to]<LOW[x]){
                LOW[x] = LOW[E[i].to];
            }
        }
        else if(is_instack[E[i].to]){
             LOW[x] = min(LOW[x],DFN[E[i].to]);
        }
    }
    if(DFN[x] == LOW[x]){
        ++cnt;
        int mid;
        int t = 0;//记录该强联通分量包含的点数 
        do{
            mid = sstack[top--];
            is_instack[mid] = false;
            Belong[mid] = cnt;
            ++t;
        }while(mid != x);
        if(t > 1)Del[cnt] = true;//如果点数大于1删除 
    }
}

int color[MAXN];

void putColor(int x){
	for(int i=head[x] ; i ; i=E[i]._next){
		if(color[E[i].to])continue;
		else {
			color[E[i].to] = color[x];
			putColor(E[i].to);
		}
	}
	for(int i=headr[x] ; i ; i=ER[i]._next){
		if(color[ER[i].to])continue;
		else {
			color[ER[i].to] = color[x];
			putColor(ER[i].to);
		}
	}
}

int DFS(int x){//用来查直接或间接说werewolf点为villager的点的数量 
	int sum = 1;
	for(int i=headr[x] ; i ; i=ER[i]._next){
		sum += DFS(ER[i].to);
	}
	return sum;
}

inline void init(){
    memset(head,0,sizeof head);
    memset(headr,0,sizeof headr);
    memset(DFN,0,sizeof(DFN));
    memset(LOW,0,sizeof(LOW));
    memset(is_instack,false,sizeof(is_instack));
    memset(Del,false,sizeof Del);
    timee = cnt = top = tot = totr = 0;
    memset(color,0,sizeof color);
}

int main(){
	
	int T;
	char s[25];
	scanf("%d",&T);
	while(T--){
		scanf("%d",&N);
		init();
		W.clear();
		if(N == 1)printf("0 0\n");
		else {
			int a;
			for(int i=1 ; i<=N ; ++i){
				scanf("%d %s",&a,s);
				if(s[0] == 'w')W.push_back(EdgeW(i,a));
				else Add(i,a);
			}
			for(int i=1 ; i<=N ; ++i)if(!DFN[i])Tarjan(i);
			//for(int i=1 ; i<=N ; ++i)printf("%d ",Belong[i]);
			for(int i=1 ; i<=N ; ++i)if(!color[i]){
				color[i] = i;
				putColor(i);
			}
			//for(int i=1 ; i<=N ; ++i)printf("%d ",color[i]);
			int sum = 0;
			for(int i=0 ; i<W.size() ; ++i){
				int from = W[i].from;
				int to = W[i].to;
				if(Del[Belong[to]] || color[from] != color[to])continue;
				else sum += DFS(to);
			}
			printf("0 %d\n",sum);
		}
	}
	
	return 0;
}

大佬神仙代码(其实速度差不多):

#include <bits/stdc++.h>

using namespace std;

typedef double db;
typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> pii;

#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define pw(x) (1ll << (x))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define rep(i,l,r) for(int i=(l);i<(r);i++)
#define per(i,l,r) for(int i=(r)-1;i>=(l);i--)
#define dd(x) cout << #x << " = " << (x) << ", "
#define de(x) cout << #x << " = " << (x) << "\n"
#define endl "\n"

//-----
const int N=100005;
pii data[N];
int ans[N],flag[N];
void dfs(int a)
{
    flag[a]=1;
    if(data[a].se)
    {
        if(!flag[data[a].fi])
        {
            dfs(data[a].fi);
            if(ans[data[a].fi]==-1)ans[a]=-1;
            else if(a==ans[data[a].fi])ans[a]=-1;
            else ans[a]=ans[data[a].fi];
        }
        else if(!ans[data[a].fi])ans[a]=N;
        else
        {
            ans[a]=ans[data[a].fi];
            if(ans[a]==a)ans[a]=-1;
        }
    }
    else ans[a]=data[a].fi;
}
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
        int T;cin>>T;
        while(T--)
        {
            int n;cin>>n;
            rep(i,1,n+1)ans[i]=0,flag[i]=0;
            rep(i,1,n+1)
            {
                int x;string s;cin>>x>>s;
                if(s=="villager")data[i]=mp(x,1);
                else data[i]=mp(x,0);
            }
            rep(i,1,n+1)if(!ans[i])dfs(i);
            int res=0;
            rep(i,1,n+1)res+=ans[i]==-1;
            //rep(i,1,n+1)cout<<ans[i]<<endl;
            cout<<"0 "<<res<<endl;
        }
    return 0;
}

 

posted @ 2018-08-08 17:19  Assassin_poi君  阅读(158)  评论(0编辑  收藏  举报