杭电多校第六场补题-1009 Werewolf
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 855 Accepted Submission(s): 222
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.
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"}
题意:狼人杀游戏中,农民只能说真话,狼人可以说真话,也可以说假话,每个人给一个描述,描述另一个人是农民还是狼人,描绘的人不能是本人,问一定是农民的或一定是狼人的有多少人。
思路:因为说真话的不一定都是农民,全部都是狼人的时候也一定成立,因为狼人真话假话都可以,所以没有人一定是农民。接下来判断谁一定是狼人。
给一个场上出的样例,1说2是农民,2说1是狼人。这样的话,可以判断出1一定是狼人,因为若1是农民,讲真话,2也是农民,讲真话,1是狼人,矛盾了。
再者说,1说2是农民,2说3是农民,3说4是农民,4说1是狼人,那么1一定是狼人,证明同上。
于是我们可以给出一个推广,如果你被一个人认定是狼人,并且这个人还是你认定的农民,或者是被你间接认定的农民,那么,你一定是狼。
再者,如果判断出我是狼人,那么说我是农民的人也一定是狼人。
因此,到这里这个题就可以写了。
我偶们使用并查集来写,看到网上有大佬缩点写的,表示敬仰。因为每个点只有一个出度,既只能说一句话,所以并查集不久可以了,指向你描绘的那个人,刚开始我们只连接进去农民边,记录出来狼人边,最后加紧去狼人边,如果狼人边的两端在同一个并查集内,则会产生狼人,首先被认定的人是狼人,其次,指向我的人是狼人,代码中v数组记录是都有谁指向我,fa[]记录的是我指向的下一个人。如果狼人边的两端在不同的并查集,可以忽略不管,为什么呢,因为咱们假如x认定y是狼人,但是y在另一个并查集中,此时咱们想如果只有y是狼人,此时x整个并查集中时农民,y并查集中有狼人,这样式成立的,但是如果x并查集中是狼人,y并查集中时农民,答案也是成立的,因此这种情况下,没有一定的狼人,代码如下:
#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> #include<vector> #include<queue> #define rep(i , j , k) for(int i=j; i<=k; i++) using namespace std; const int maxn = 100005; int t, n, fa[maxn]; int ans; vector<int>V , v[maxn]; queue<int>Q; void init() { ans = 0; V.clear(); rep(i, 1, n) { fa[i] = i; v[i].clear(); } while( !Q.empty() ) { Q.pop(); } } int fin(int x) { if(x == fa[x]) return x; return fa[x] = fin(fa[x]); } void join(int u, int v) { int fu, fv; fu = fin(u); fv = fin(v); if(fu != fv) { fa[fu] = fv; } } int main() { scanf("%d", &t); while( t-- ) { scanf("%d", &n); init(); int tmp; char s[20]; rep(i, 1, n) { scanf("%d", &tmp); scanf("%s", s); if(s[0] == 'v') { join(i, tmp); v[tmp].push_back(i); } else { V.push_back(i); V.push_back(tmp); } } rep(i, 0, V.size()-1) { int a, b; a = V[i]; b = V[++i]; if(fin(a) ==fin(b)) { Q.push(b); while( !Q.empty() ) { int f = Q.front(); Q.pop(); ans++; for(int j=0; j<v[f].size(); j++) { Q.push(v[f][j]); } } } } if(n == 1) { printf("0 0\n"); } else { printf("0 %d\n", ans); } } return 0; }