cf776 D 并查集

题目大意就是有n个门,m个机关,机关对应的是k个门,点击机关m则k个门的状态全部反转。询问是否存在一种方式让所有门都变为1;每个门都是只有两个机关可影响到他。

分析一下问题可以知道,对于门初始为1,则这个门对应的两个机关只能一起选,或者都不选,也就是这两个机关是被绑定着的。
对于门初始为0,则这个们对应的两个机关只能选一个,选了A就不能选B,选B,就不能选A;
考虑机关只有选与不选对不对。我们这么去假设,设置种类并查集pre[i]和pre[i + m],pre[i]指的是i的同类,pre[i + m]是指i的敌对面。对于上面的每个机关,我们都可以对应的去连线。
(1)对于门i为1,t1,t2为两个与门i有关联的门。t1与t2去合并,t1+m与t2+m去合并,(t1与t2是同类,t1的对立面也就是t2的对立面)
(2)对于门i为0,t1,t2为两个与门i有关联的门。t1与t2+m去合并,t1 + m与t2去合并,这样就表明了他们的敌对关系。
怎么判断no呢?就是上面的(1)(2)出现矛盾的时候就直接NO;比如你t1要和t2是敌对,但是最后的结果表名他们是同类,则直接NO;

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<time.h>
#include<string>
#include<cmath>
#include<stack>
#include<map>
#include<set>
#define ll long long
#define double long double
using namespace std;
#define PI 3.1415926535898
#define eqs 1e-10
const long long max_ = 1e5 + 7;
int mod = 10000;
const int inf = 1e9 + 7;
const long long INF = 1e18;
int read() {
	int s = 0, f = 1;
	char ch = getchar();
	while (ch<'0' || ch>'9') {
		if (ch == '-')
			f = -1;
		ch = getchar();
	}
	while (ch >= '0'&&ch <= '9') {
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}
inline int min(int a, int b) {
	return a < b ? a : b;
}
inline int max(int a, int b) {
	return a > b ? a : b;
}
int pre[max_ * 3];
int get(int x) {
	if (pre[x] == x)return x;
	return pre[x] = get(pre[x]);
}
int n, m,sta[max_];
vector<int> switch_[max_];
vector<int> no[max_];
void hb(int a, int b) {
	int t1 = get(a), t2 = get(b);
	if(t1 != t2)pre[t1] = t2;
}
signed main() {
	n = read(), m = read();
	for (int i = 1; i <= 2 * m; i++) {pre[i] = i;}
	for (int i = 1; i <= n; i++) {
		sta[i] = read();
	}
	for (int i = 1; i <= m; i++) {
		int num = read();
		while (num--){
			int t = read(); switch_[i].push_back(t);
			no[t].push_back(i);
		}
	}
	for (int i = 1; i <= n; i++) {
		if (sta[i]) {
			//按两次
			hb(no[i][0], no[i][1]);
			hb(no[i][0] + m, no[i][1] + m);
		}
		else {
			//按一次
			hb(no[i][0], no[i][1] + m);
			hb(no[i][0] + m, no[i][1]);
		}
	}
	for (int i = 1; i <= n; i++) {
		if (!sta[i]) {
			//按一次
			int t1 = get(no[i][0]), t2 = get(no[i][1]);
			if (t1 == t2) {
				cout << "NO\n"; return 0;
			}
		}
	}
	cout << "YES\n";
	return 0;
}

全部评论

相关推荐

勇敢的联想人前程似锦:如果我是你,身体素质好我会去参军,然后走士兵计划考研211只需要200多分。
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务