• 【带权并查集 + DP】AcWing 259. 真正的骗子


    这题属实逆天。。题面在输出格式中没有说明需要将编号排序后输出,让我困惑了半天呜呜。

    分析

    题目本身的思路是很简单的。

    我们从一个人说 yesno 能够得到什么呢?

    • 假设这个人是天神,那么说 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;
    }
    
  • 相关阅读:
    Storm
    Linux 网络配置
    .NET Remoting
    jwt
    new操作符
    元数据
    C# lock
    三十而立
    面试
    灯火
  • 原文地址:https://www.cnblogs.com/Tenshi/p/15859836.html
Copyright © 2020-2023  润新知