题目大意
给定门和钥匙的数量,每把钥匙控制 \(k_i\) 扇门,每扇门被两把钥匙控制。
给定初始时每扇门的状态,求是否存在一种方法使得所有的门都打开。
思路
扩展域并查集。
考虑分类讨论:
- 对于开着的门,要么两把钥匙都用,要么两把钥匙都不用;
- 对于关着的门,两把钥匙只能用一把。
那么我们就可以用并查集来进行维护了。
设 \(i\) 为用第 \(i\) 把钥匙,\(i+m\) 为不用第 \(i\) 把钥匙。
对于一扇门,我们记它的两把钥匙为 \(u,v\)。
如果这扇门开着,我们将 \(u\) 与 \(v\) 合并,\(u + m\) 与 \(v+m\) 合并;
如果这扇门关着,我们将 \(u\) 与 \(v+m\) 合并,\(u + m\) 与 \(v\) 合并。
最后,我们检查 \(i\) 与 \(i + m\) 是否在同一集合,如果有在同一集合的,输出 NO
,否则输出 YES
。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 200500;
int n,m,k[N];
int state[N];
vector<int> key[N];
int fa[N << 1];
class DSU{
public:
int Find(int x) {
if(fa[x] == x)
return x;
return fa[x] = Find(fa[x]);
}
void Merge(int x,int y) {
x = Find(x);
y = Find(y);
fa[x] = y;
return ;
}
bool Check(int x,int y) {
x = Find(x);
y = Find(y);
if(x == y)
return 1;
return 0;
}
}dsu;
int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 1;i <= n; i++)
cin >> state[i];
for(int i = 1,num,x;i <= m; i++) {
cin >> num;
fa[i] = i;
fa[i + m] = i + m;
for(int j = 1;j <= num; j++) {
cin >> x;
key[x].push_back(i);
}
}
for(int i = 1;i <= n; i++) {
int u = key[i][0],v = key[i][1];
if(state[i]) {
dsu.Merge(u,v);
dsu.Merge(u + m,v + m);
}
else {
dsu.Merge(u,v + m);
dsu.Merge(u + m,v);
}
}
for(int i = 1;i <= m; i++) {
if(dsu.Check(i,i + m)) {
cout << "NO\n";
return 0;
}
}
cout << "YES\n";
return 0;
}
标签:Door,int,合并,fa,钥匙,CF776D,两把,Problem,扇门
From: https://www.cnblogs.com/baijian0212/p/solution-cf776d.html