2-SAT 学习笔记

对于一个命题:如果a,那么b
其逆命题为:如果b,那么a
其否命题为:如果¬a,那么¬b
其逆否命题为:如果¬b,那么¬a
∧表示与,∨表示或,¬表示非
原命题与逆否命题等值。逆命题与否命题等值
原命题与逆命题以及逆否命题与否命题之间没有关系

那么只需要 \(a -> b\) \(!b -> !a\) 就可以了

2-SAT板子

考虑 \(x_i\) 只有两个状态
此题说的是两者满足其一
那么可以转化成 满足前者且不满足后者 的关系
所以就这样了。

#include
using namespace std ;
int n , m ;
const int N = 1e6 + 10 ;
struct node { int v , nxt ; } e[N << 1] ;
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 ;
int st[N << 1] , tp = 0 ;
int 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[u] , 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) ;
  cin >> n >> m ;
  for(register int i = 1 ; i <= m ; i ++) {
    int a , f , b , f2 ;
    cin >> a >> f >> b >> f2 ;
    add(a + n * (f ^ 1) , b + n * (f2 & 1)) ;
    add(b + n * (f2 ^ 1) , a + n * (f & 1)) ;
  }
  for(register int i = 1 ; i <= (n << 1) ; i ++)
    if(! dfn[i]) tarjan(i) ;
  for(register int i = 1 ; i <= n ; i ++)
    if(co[i] == co[n + i]) { puts("IMPOSSIBLE") ; return 0 ; }
  cout << "POSSIBLE\n" ;
  for(register int i = 1 ; i <= n ; i ++) {
    if(co[i] > co[i + n]) cout << 1 << ' ' ;
    else cout << 0 << ' ' ;
  }
  return 0 ;
}

你可能感兴趣的:(2-SAT 学习笔记)