Codeforces 776D.The Door Problem (dfs二分图判定 / 并查集)

Posted tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces 776D.The Door Problem (dfs二分图判定 / 并查集)相关的知识,希望对你有一定的参考价值。

题目链接:

http://codeforces.com/problemset/problem/776/D

题意:

n扇门,m个开关(n,m<=1e5),每个开关控制若干个门,反转开关门状态变化,每个门正好被两个开关控制,问是否有可能把所有门的状态置为1?

思路:

from: http://blog.csdn.net/jeremy1149/article/details/56839453

方法一:二分图染色

注意到一个门只被两个开关控制 若门初始为0 则控制它的两个开关状态相反,门初始为1则 要求控制的两个开关状态相同,才能可以把门置为1.

以开关为顶点,(u,v)的边为被(u,v)控制的门的初始状态, dfs做二分图染色,判断每条边(每扇门)是否都能被满足

方法二:并查集

把每一个钥匙拆成两个点x,x+m,分别表示选不选这把钥匙。
我们知道一扇门一定对应了两把钥匙。
设一扇门对应的要是分别为u,v,link(x,y)表示点x向点y连边。
如果这扇门要操作一次,那就是两把当中选一把:link(u,v+m),link(v,u+m)。这表示的是如果我选了拿第u把钥匙就不能拿第v把钥匙,如果我选了拿第v把钥匙就不能拿第u把钥匙
如果这扇门不要操作,那就是两把当中选两把或者都不选:link(u+m,v+m),link(v,u)。这表示的是如果我选了拿第u把钥匙就一定要拿第v把钥匙,如果我不拿第v把钥匙就一定不能拿第u把钥匙
这个是无向边啊,并查集维护一下关系即可。
对于1...m中的每一把钥匙,如果x,x+m属于一个连通块就无解了,因为包含关系成环。

代码:

代码一:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 1e5+10;

int n,k,st[maxn],c[maxn],flag;
vector<int> p[maxn];
vector<pair<int,int> > g[maxn];

void dfs(int u,int x){
    if(flag == false) return ;
    if(c[u] != -1){
        if(c[u] != x) flag=false;
        return ;
    }
    c[u] = x;
    for(int i=0; i<(int)g[u].size(); i++){
        int v = g[u][i].first, w = g[u][i].second;
        int y;
        if(w == 1) y = x;
        else y = x^1;
        dfs(v,y);
    }
}

int main(){
    cin >> n >> k;
    for(int i=1; i<=n; i++)
        st[i] = read();
    for(int i=1; i<=k; i++){
        int num = read();
        while(num--){
            int x = read();
            p[x].push_back(i);
        }
    }
    for(int i=1; i<=n; i++){
        int u = p[i][0], v = p[i][1];
        g[u].push_back(MP(v,st[i]));
        g[v].push_back(MP(u,st[i]));
    }
    flag = 1;
    memset(c,-1,sizeof(c));
    for(int i=1; i<=k; i++){
        if(c[i] == -1) dfs(i,0);
    }

    if(flag) puts("YES");
    else puts("NO");
    return 0;
}

代码二:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define MS(a) memset(a,0,sizeof(a))
#define MP make_pair
#define PB push_back
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1;ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x*f;
}
//////////////////////////////////////////////////////////////////////////
const int maxn = 2e5+10;

int n,k,st[maxn],fa[maxn];
vector<int> p[maxn];

int find(int x){
    return fa[x]==x ? x : fa[x]=find(fa[x]);
}

void merge(int u,int v){
    int p1 = find(u), p2 = find(v);
    if(p1==p2) return ;
    fa[p1] = p2;
}

int main(){
    cin >> n >> k;
    for(int i=1; i<=n; i++)
        st[i] = read();
    for(int i=1; i<=k; i++){
        int num = read();
        for(int j=0; j<num; j++){
            int x = read();
            p[x].push_back(i);
        }
    }
    for(int i=1; i<=2*k; i++) fa[i]=i;
    for(int i=1; i<=n; i++){
        int u = p[i][0], v = p[i][1];
        if(st[i]){
            merge(u,v);
            merge(u+k,v+k);
        }else{
            merge(u,v+k);
            merge(u+k,v);
        }
    }
    for(int i=1; i<=k; i++){
        if(find(i) == find(i+k)){
            puts("NO");
            return 0;
        }
    }
    puts("YES");

    return 0;
}

 

以上是关于Codeforces 776D.The Door Problem (dfs二分图判定 / 并查集)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces 776D:The Door Problem(DFS染色)

CodeForces 776D The Door Problem并查集

CF776D The Door Problem

codeforces 776E The Holmes Children

CodeForces 776E 数学规律,欧拉

CodeForces 776B Sherlock and his girlfriend