CF776D The Door Problem

发布时间 2023-08-15 19:10:42作者: -白简-

题目大意

给定门和钥匙的数量,每把钥匙控制 \(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;
}