CF776D The Door Problem [2sat]
考虑 \(\texttt{2-SAT}\)
首先每个门 \(i\) 都有一个初始状态 \(a_i\)
题目条件每个门只被两个开关控制,那么很显然的 \(\texttt{2-SAT}\)
用 \(b_{i,{0/1}}\)记录是第 \(1/2\) 个开关
然后就考虑一下门的初始状态 \(a_i\)
- 门本身是开的
你开这个开关为开,另一个也要是开的,
反之亦然,所以建两对双向边
- 门本身是关的
你开这个开关为开,另一个必须是关的
反之亦然,所以还是建两对双向边
然后根据 \(\texttt{2-SAT}\) 的性质,可得答案
#include<bits/stdc++.h>
using namespace std ;
int n , m ;
const int N = 2e5 + 10 ;
int a[N] ;
int b[N][2] ;
struct Edge { int v , nxt ; } e[N << 2] ;
int head[N << 1] , cnt = 0 ;
inline void add(int u , int v) {e[++ cnt] = { v, head[u]} ; head[u] = cnt ; }
int dfn[N << 1] , low[N << 1] , idx = 0 , st[N << 1], tp = 0 , co[N << 1] , num = 0 ;
inline void tarjan(int u) { dfn[u] = low[u] = ++ idx ; st[++ tp] = u ;
for(register int i = head[u] ; i ; i = e[i].nxt) { int v = e[i].v ;
if(! dfn[v]){ tarjan(v) ; low[u] = min(low[u] , low[v]) ; }
else if(! co[v]) { low[u] = min(low[v] , dfn[v]) ; }
} if(low[u] == dfn[u]) { co[u] = ++ num ; while(st[tp] ^ u) co[st[tp --]] = num ; tp -- ; }
}
signed main() {
#ifdef _WIN64
freopen("0.in" , "r" , stdin) ;
#endif
ios :: sync_with_stdio(false) ; cin.tie(nullptr) ; cout.tie(nullptr) ;
memset(a , 0 , sizeof(a)) ; memset(b , 0 , sizeof(b)) ;
cin >> n >> m ;
for(register int i = 1 ; i <= n ; i ++) { cin >> a[i] ; }
for(register int i = 1 ; i <= m ; i ++) { int t ; cin >> t ;
for(register int j = 1 ; j <= t ; j ++) { int x ; cin >> x ; if(b[x][0]) { b[x][1] = i ; } else b[x][0] = i ; }
}
for(register int i = 1 ; i <= n ; i ++) {
if(a[i]) { add(b[i][0] , b[i][1]) ; add(b[i][1] , b[i][0]) ; add(b[i][0] + m , b[i][1] + m) ; add(b[i][1] + m , b[i][0] + m) ; }
else { add(b[i][0] , b[i][1] + m) ; add(b[i][1] + m , b[i][0]) ; add(b[i][1] , b[i][0] + m) ; add(b[i][0] + m , b[i][1]) ; }
}
for(register int i = 1 ; i <= m * 2 ; i ++) { if(! dfn[i]) tarjan(i) ; }
for(register int i = 1 ; i <= m ; i ++) if(co[i] == co[i + m]) { return cout << "NO\n" , 0 ; }
cout << "YES\n" ;
return 0 ;
}