P4782 【模板】2-SAT 問題
阿新 • • 發佈:2021-07-25
將每個數的真假分成兩個點$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 namespacestd; 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; }