1. 程式人生 > 其它 >P4782 【模板】2-SAT 問題

P4782 【模板】2-SAT 問題

P4782 【模板】2-SAT 問題

將每個數的真假分成兩個點$i+n,i$

$a$真或$b$假

$=a$假$b$必假,$b$真$a$必真

$=a->b,b+n->a+n$

以此類推建邊

如果$x,x+n$在同一個強連通分量裡,就不成立

用Tarjan判斷。

注意有$2n$個點,每個都要跑Tarjan

詢問可行做法時,我們總是取$x,x+n$中拓撲序靠後的那個

也就是連通塊編號小的(因為是dfs)

連邊時用的是位運算

#include<iostream>
#include<cstdio>
#include<vector>
using namespace
std; const int N=2000003; vector <int> g[N]; int n,m,dfn[N],low[N],K,be[N],B,st[N],tp; void Tar(int x){ dfn[x]=low[x]=++K; st[++tp]=x; for(int i:g[x]){ if(!dfn[i]) Tar(i),low[x]=min(low[x],low[i]); else if(!be[i]) low[x]=min(low[x],dfn[i]); } if(dfn[x]==low[x]){ be[x]
=++B; for(;st[tp]!=x;--tp) be[st[tp]]=B; --tp; } } int main(){ scanf("%d%d",&n,&m); for(int i=1,q,w,e,r;i<=m;++i){ scanf("%d%d%d%d",&q,&w,&e,&r); g[q+n*(w^1)].push_back(e+n*r); g[e+n*(r^1)].push_back(q+n*w); } for
(int i=1;i<=n*2;++i) if(!dfn[i]) Tar(i); for(int i=1;i<=n;++i) if(be[i]==be[i+n]){ puts("IMPOSSIBLE"); return 0; } puts("POSSIBLE"); for(int i=1;i<=n;++i) printf("%d ",be[i]>be[i+n]); return 0; }