题目:传送门
思路:带权并查集,分出若干个集合,每个集合缩成两个权值,如果唯一存在 每个集合中的一个权值相加后等于天使的个数,那么就能够判断出哪些是天使。这里我们可以用一个DP(i,j)表示前i个集合能组成 j 的方案数,最后记录一下路径,把每个集合的一些点加入答案中。 需要注意的是这里的初值是只有DP(0,0)才有意义,因为是恰好组成 j 的方案数,因此不能用滚动数组优化。
#include<cstdio> #include<vector> #include<cstring> #include<cctype> #include<functional> #include<cmath> #include<algorithm> #pragma GCC optimize(2) using namespace std; typedef long long LL; typedef pair<int,int> pii; typedef pair<double,double> pdd; const int N=1e3+5; const int inf=0x3f3f3f3f; const int mod=998244353; const double eps=1e-9; const long double pi=acos(-1.0L); #define ls (i<<1) #define rs (i<<1|1) #define fi first #define se second #define pb push_back #define mk make_pair #define mem(a,b) memset(a,b,sizeof(a)) LL read() { LL x=0,t=1; char ch; while(!isdigit(ch=getchar())) if(ch=='-') t=-1; while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); } return x*t; } int n,p1,p2,tot; int w0[N],w1[N],f[N],v[N],dp[N][N],p[N]; int getf(int x) { if(x!=f[x]) { int t=f[x]; f[x]=getf(f[x]); v[x]+=v[t]; v[x]%=2; } return f[x]; } inline int init() { tot=0; for(int i=1;i<=p1+p2;i++) f[i]=i,v[i]=0; mem(dp,0); mem(w0,0); mem(w1,0); } int main() { while(scanf("%d%d%d",&n,&p1,&p2)!=EOF&&(n||p1||p2)) { init(); for(int i=1;i<=n;i++) { int x=read(),y=read(); char z[5]; scanf("%s",z); int t=z[0]=='n'; int fx=getf(x),fy=getf(y); if(fx!=fy) { v[fy]=(-v[y]+t+v[x]+2)%2; f[fy]=fx; } } for(int i=1;i<=p1+p2;i++) { if(i!=getf(i)) continue; int r=i; p[++tot]=r; for(int j=1;j<=p1+p2;j++) { if(r!=getf(j)) continue; if(v[j]%2==v[r]%2) w0[tot]++; else w1[tot]++; } } dp[0][0]=1; for(int i=1;i<=tot;i++) for(int j=p1;j>=0;j--) { if(j>=w0[i]) dp[i][j]+=dp[i-1][j-w0[i]]; if(j>=w1[i]) dp[i][j]+=dp[i-1][j-w1[i]]; }//不能用滚动数组优化,因为只有(0,0)为1; if(dp[tot][p1]!=1) { printf("no "); continue; } int k=p1; vector<int> ans; for(int i=tot;i&&k;i--) { if(k>=w0[i]&&dp[i-1][k-w0[i]]==1) { k-=w0[i]; int r=p[i]; for(int j=1;j<=p1+p2;j++) if(r==getf(j)&&v[j]==0) ans.pb(j); } else if(k>=w1[i]&&dp[i-1][k-w1[i]]==1) { k-=w1[i]; int r=p[i]; for(int j=1;j<=p1+p2;j++) if(r==getf(j)&&v[j]==1) ans.pb(j); } } sort(ans.begin(),ans.end()); for(int i=0;i<ans.size();i++) printf("%d ",ans[i]); printf("end "); } return 0; } /* 4 4 2 1 2 yes 3 6 no 4 3 no 1 5 yes */