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; }