[//]: # (推薦題解模板,請替換blablabla等內容 ^^)
### 題目描述
一個島上存在着兩種居民,一種是天神,一種是惡魔。
天神永遠都不會說假話,而惡魔永遠都不會說真話。
島上的每一個成員都有一個整數編號(類似於身份證號,用以區分每個成員)。
現在你擁有n次提問的機會,但是問題的內容只能是向其中一個居民詢問另一個居民是否是天神,請你根據收集的回答判斷各個居民的身份。
輸入格式
輸入包含多組測試用例。
每組測試用例的第一行包含三個非負整數n,p1,p2p1,p2,其中n是你可以提問的總次數,p1p1是天神的總數量,p2p2是惡魔的總數量。
接下來n行每行包含兩個整數xi,yixi,yi以及一個字符串aiai,其中xi,yixi,yi是島上居民的編號,你將向編號為xixi的居民詢問編號為yiyi的居民是否是天神,
aiai是他的回答,如果aiai為“yes”,表示他回答你“是”,如果aiai為“no”,表示他回答你“不是”。
xi,yixi,yi可能相同,表示你問的是那個人自己是否為天神。
當輸入為占據一行的“0 0 0”時,表示輸入終止。
輸出格式
對於每組測試用例,如果詢問得到的信息足以使你判斷每個居民的身份,則將所有天神的編號輸出,每個編號占一行,在輸出結束后,在另起一行輸出“end”,表示該用例輸出結束。
如果得到的信息不足以判斷每個居民的身份,則輸出“no”,輸出同樣占一行。
數據范圍
1≤xi,yi≤q1+q21≤xi,yi≤q1+q2,
1≤n<1000,1≤p1,p2<300
#### 樣例
```
輸入樣例:
2 1 1 1 2 no 2 1 no 3 2 1 1 1 yes 2 2 yes 3 3 yes 2 2 1 1 2 yes 2 3 no 5 4 3 1 2 yes 1 3 no 4 5 yes 5 6 yes 6 7 no 0 0 0
輸出樣例:
no no 1 2 end 3 4 5 6 end
```
----------
### 算法1
##### (並查集+dp+路徑還原)
前置知識 並查集(食物鏈 那道題要會做),dp的路徑還原(可以狀態回溯)
1.對於 a b yes, 我們合並(a,b)和(a + p1 + p2, b + p1 + p2)
代表a,b一體,如果a是好人則b是好人,如果a(a + p1 + p2)是壞人,則b(b + p1 + p2)是壞人
2.對於 a b no, 合並(a, b + p1 + p2), (a + p1 + p2, b)含義和上面一樣
僅有人之間的指認關系是不知道答案的,我們要找幾個集合,恰好且僅有一種方法能湊出 p1 人才有答案
對於每個人,要么是好人,要么是壞人(廢話,下面是關鍵)
所以對於每個人,
1.這個人 i 所在的集合全是好人,與 i + p1 + p2 處於一個集合的都是壞人
即father[其他人] == father[i] 的 都是好人, father[其他人] == father[i + p1 + p2] 的 都是壞人
2. 這個人 i 是壞人,………………
每個大集合分為,這個集合的father是好人, father是壞人
所以dp的時候必須從上個狀態轉移(不存在這個人及不是好人也不是壞人在狀態轉換)不能直接繼承
對於前i個集合可拼成 j個好人, 轉移:
dp[i][j] += dp[i - 1][j - 集合的father是好人時好人人數]
dp[i][j] += dp[i - 1][j - 集合的father是壞人時好人人數]
dp[i][j] 存的是能滿足前i個集合可拼成 j個好人的方法數,只有當(dp[集合個數][p1] == 1)能唯一確定誰是好人,誰是壞人
dp狀態的回溯見代碼,畢竟 (dp[集合個數][p1] == 1) 路徑是唯一的很簡單
#### C++ 代碼
```
#include <bits/stdc++.h> using namespace std; int f[1205], s[1205], n, x, y, sum, h[1205]; int tot, a[605], vis[1205], b[605], dp[605][605]; char str[5]; int find(int x) { if (x == f[x]) return x; return f[x] = find(f[x]); } void unit(int x, int y) { x = find(x), y = find(y); if (x == y) return; if (h[x] > h[y]) s[x] += s[y], f[y] = x; else if (h[x] < h[y]) s[y] += s[x], f[x] = y; else s[x] += s[y], f[y] = x, ++ h[x]; } void init() { memset(dp, 0, sizeof dp); sum = x + y, tot = 0, dp[0][0] = 1; for (int i = 1; i<= sum; ++ i) { f[i] = i, f[i + sum] = i + sum; s[i] = h[i] = h[i + sum] = 1; s[i + sum] = vis[i] = vis[i + sum] = 0; } } int main() { while(scanf("%d%d%d", &n, &x, &y), n + x + y) { init(); for (int i = 1, a, b; i <= n; ++ i) { scanf("%d%d%s", &a, &b, str + 1); if (str[1] == 'y') unit(a, b), unit(a + sum, b + sum); else unit(a, b + sum), unit(a + sum, b); } for (int i = 1, fi; i<= sum; ++ i) { if((fi = find(i)) != i) continue; vis[fi] = vis[fi + sum] = ++ tot; a[tot] = s[fi], b[tot] = s[fi + sum]; } for (int i = 1; i <= tot; ++ i) { for (int j = min(a[i], b[i]); j <= x; ++ j) { if (j >= a[i]) dp[i][j] += dp[i - 1][j - a[i]]; if (j >= b[i]) dp[i][j] += dp[i - 1][j - b[i]]; } } if (dp[tot][x] != 1) {puts("no"); continue;} for (int i = tot; i; -- i) if (dp[i - 1][x - a[i]]) x -= a[i], a[i] = -1; else if (dp[i - 1][x - b[i]]) x -= b[i], a[i] = -2; for (int i = 1, fi = find(1); i <= sum; fi = find(++ i)) if(vis[fi]) if (fi > sum && a[vis[fi]] == -2) printf("%d\n", i); else if (fi <= sum && a[vis[fi]] == -1) printf("%d\n", i); puts("end"); } return 0; }
```
----------