这题属实逆天。。题面在输出格式中没有说明需要将编号排序后输出,让我困惑了半天呜呜。
分析
题目本身的思路是很简单的。
我们从一个人说 yes
和 no
能够得到什么呢?
- 假设这个人是天神,那么说
yes
说明对方也是天神,否则是恶魔。 - 假设这个人是恶魔,那么说
yes
说明对方也是恶魔,否则是天神。
这说明,yes
意味着该人和对方身份相同,no
则意味着不同。
我们便可以自然地想到使用带权并查集来维护人与人的关系,如果二人身份相同,那么之间的权值应该是 \(0\),否则为 \(1\)。
使用并查集将题目给的关系处理出来后,我们可以得到 \(tot\) 个连通块,记第 \(k\) 个连通块和根节点(也就是并查集中每棵树的 \(root\))距离为 \(0\) 的人数为 \(a_k\),距离为 \(1\) 的人数为 \(b_k\)。
现在的问题变成:给你一个 \(X\)(也就是题目的天神人数),问是否存在且唯一存在一种决策,使得对于每个位置 \(k\) 从 \(a_k, b_k\) 二选一,能够让最后选出的值和等于 \(X\)。
我们采取 DP 来解决这个问题,定义 \(dp(i, j)\) 为选到第 \(i\) 个位置时能够填满容量为 \(j\) 的背包的方案数,那么转移方程是:
\[dp(i, j) += dp(i-1, j-a_i) (\texttt{选取 $a_i$},这里 a_i\neq 0) \] \[dp(i, j) += dp(i-1, j-b_i) (\texttt{选取 $b_i$},这里 b_i\neq 0) \]显然,如果 \(dp(tot, X)=1\) 就是有解,反之无解。
这题还需要输出方案,我们再开一个 \(pre\) 数组记录一下转移的过程。
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;
inline void read(int &x){
int s=0; x=1;
char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
x*=s;
}
const int N=630;
int m, X, Y, n;
int f[N], d[N];
int find(int x){
if(x==f[x]) return x;
int root=find(f[x]);
d[x]^=d[f[x]];
return f[x]=root;
}
int tot;
int a[N], b[N];
int main(){
while(cin>>m>>X>>Y, m || X || Y){
n=X+Y;
rep(i,1,n) f[i]=i, d[i]=0;
rep(i,1,m){
int u, v; read(u), read(v); string s; cin>>s;
int pu=find(u), pv=find(v);
if(pu!=pv){
d[pu]=d[u]^d[v]^(s=="no");
f[pu]=pv;
}
}
vector<int> g[n+5];
map<int, int> mp;
tot=0;
rep(i,1,n) g[find(i)].pb(i);
rep(i,1,n) if(g[i].size()){
mp[++tot]=i;
a[tot]=b[tot]=0;
for(auto j: g[i]){
if(!d[j]) a[tot]++;
else b[tot]++;
}
}
int dp[tot+5][n+5]; memset(dp, 0, sizeof dp);
int pre[tot+5][n+5]; memset(pre, 0, sizeof pre);
dp[0][0]=1;
rep(i,1,tot){
rep(j,a[i],X){
dp[i][j]+=dp[i-1][j-a[i]];
if(dp[i-1][j-a[i]]) pre[i][j]=j-a[i];
}
rep(j,b[i],X){
dp[i][j]+=dp[i-1][j-b[i]];
if(dp[i-1][j-b[i]]) pre[i][j]=j-b[i];
}
}
if(dp[tot][X]!=1){
puts("no");
continue;
}
int u=X, idx=tot;
vector<int> buf;
while(1){
int go=pre[idx][u];
if(go+a[idx]==u) buf.pb(0);
else buf.pb(1);
u=go;
if(--idx==0) break;
}
reverse(all(buf));
vector<int> res;
rep(i,0,tot-1){
int id=mp[i+1];
if(!buf[i]){
for(auto e: g[id]) if(!d[e]) res.pb(e);
}
else{
for(auto e: g[id]) if(d[e]) res.pb(e);
}
}
sort(all(res));
for(auto i: res) cout<<i<<endl;
puts("end");
}
return 0;
}