牛客 contest893 G-Truthman or Fakeman

题目链接

N N N个人, M M M句话。每一句话这样描述 ( U , V , W ) (U,V,W) (U,V,W) U U U 认为 V V V T r u t h m a n Truthman Truthman或者 F a k e m a n Fakeman Fakeman
根据这 M M M句话求 T r u t h m a n Truthman Truthman最多的一种情况,误解输出 − 1 -1 1

思路

一共有两种情况:

  • U U U 认为 V V V T r u t h m a n Truthman Truthman U , V U,V U,V同时为真或同时为假。
  • U U U 认为 V V V F a k e m a n Fakeman Fakeman U , V U,V U,V一人为真一人为假。
  1. 我么可以把同一类的人用并查集合到一起, 然后判断是否有解。
  2. 如何构造 T r u t h m a n Truthman Truthman最多的情况?我们可以在并查集中为每个人定义一个权值或者是标记,记录这个人是 T r u t h m a n Truthman Truthman还是 F a k e m a n Fakeman Fakeman,这样并查集就可以比较那个 T r u t h m a n Truthman Truthman的人多。
#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define lc rt<<1
#define rc rt<<1|1
#define endl '\n'
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
using namespace std;
int vis[maxn<<1], sz[maxn<<1], pre[maxn<<1];
int ans[maxn], n, m;
int find (int x) {
	return (x == pre[x]) ? x : pre[x] = find(pre[x]);
}
void join(int x, int y) {
	int fx = find(x);
	int fy = find(y);
	if (fx == fy) return;
	pre[fy] = fx;
	sz[fx] += sz[fy];
}

void solve() {
	for (int i = 1; i <= n; ++i) {
		int fx = find(i); 
		int fy = find(i+n);
		if (fx == fy) {
			cout << -1 << endl;
			return;
		}
		// 判断当前所在的并查集是否已经确定 Truth or Fake
		if (vis[fx] == 0) {
			// 根据并查集的权值确定属性
			if (sz[fx] > sz[fy]) {
				vis[fx] = 1;
				vis[fy] = -1;
			}else {
				vis[fy] = 1;
				vis[fx] = -1;
			}
		}
		ans[i] = (vis[fx] == 1);
	}
	for (int i = 1; i <= n; ++i) {
		cout << ans[i];
	}
	cout << endl;
}
int main () {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T;
    cin >> T;
    while (T--) {	
	    cin >> n >> m;
	    for (int i = 1; i <= n*2; ++i) {
    		pre[i] = i;
    		sz[i] = (i <= n);
    		vis[i] = 0;
    	}
	    for (int i = 0, u,v,w; i < m; ++i) {
	    	cin >> u >> v >> w;
	    	// 合并同类人
	    	if (w) {
	    		join(u, v);
	    		join(u+n, v+n);
	    	}else {
	    		join(u, v+n);
	    		join(u+n, v);
	    	}
	    }
	    solve();
    }
    return 0;
}	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值