题目来源:http://codeforces.com/problemset/problem/776/D
思路来自:https://blog.youkuaiyun.com/black_miracle/article/details/57083033
对每个开关i,有两种状态,即开或不开。可以用i表示开,i+m表示不开。
对于每个门,与门相连的两个开关为a和b,若门的状态为1,则(a,b) (a+m,b+m) 应相同。
门的状态为0,(a,b+m) (a+m,b)应相同。
用并查集维护。如果最终每个门对应的开关a与a+m所属并查集相同,则一定无解。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int n,m,p[maxn*4];
bool vis[maxn];
vector<int> v[maxn];
int find(int x) {
return x == p[x] ? p[x] : p[x] = find(p[x]);
}
void linkit(int u,int v) {
int x = find(u), y = find(v);
if (x != y)
p[x] = y;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> vis[i];
}
for (int i = 1; i <= m; ++i) {
int x;
cin >> x;
for (int j = 1; j <= x; ++j) {
int t;
cin >> t;
v[t].push_back(i);
}
}
for (int i = 1; i <= m * 2; ++i) {
p[i] = i;
}
for (int i = 1; i <= n; ++i) {
if (vis[i]) {
linkit(v[i][0], v[i][1]);
linkit(v[i][0] + m, v[i][1] + m);
} else {
linkit(v[i][0] + m, v[i][1]);
linkit(v[i][0], v[i][1] + m);
}
}
bool ok = 1;
for (int i = 1; i <= m; ++i) {
if (find(i) == find(i + m)) {
ok = 0;
break;
}
}
if (ok)puts("YES");
else puts("NO");
return 0;
}