题目
题目链接:https://www.luogu.com.cn/problem/P4782
有 \(n\) 个布尔变量 \(x_1\)\(\sim\)\(x_n\),另有 \(m\) 个需要满足的条件,每个条件的形式都是 「\(x_i\) 为 true
/ false
或 \(x_j\) 为 true
/ false
」。比如 「\(x_1\) 为真或 \(x_3\) 为假」、「\(x_7\) 为假或 \(x_2\) 为假」。
2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。
思路
对于一个要求 「\(x\) 为 true
或 \(y\) 为 true
」,它的意思就是 「如果 \(x\) 为 false
那么 \(y\) 必须是 true
,如果 \(y\) 为 false
那么 \(x\) 必须是 false
」。
把每个点拆成 true
和 false
两个点。在上例中,我们就从 \(x_f\) 向 \(y_t\) 连边,\(y_f\) 向 \(x_t\) 连边。
那么如果若干个点在一个强连通分量内,那么他们的取值是一样的。
所以 tarjan 缩点,记录每一个点属于哪一个强连通分量 \(col[x]\),显然如果 \(col[x_t]=col[x_f]\),那么无解。
代码
#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2000010;
int n,m,cnt,tot,head[N],dfn[N],low[N],col[N];
bool vis[N];
stack<int> st;
struct edge
{
int next,to;
}e[N*2];
void add(int from,int to)
{
e[++tot].to=to;
e[tot].next=head[from];
head[from]=tot;
}
void tarjan(int x)
{
dfn[x]=low[x]=++tot;
st.push(x); vis[x]=1;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (!dfn[v])
{
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if (vis[v])
low[x]=min(low[x],dfn[v]);
}
if (dfn[x]==low[x])
{
cnt++; int y;
do {
y=st.top(); st.pop();
col[y]=cnt; vis[y]=0;
} while (x!=y);
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for (int i=1,x,y,fx,fy;i<=m;i++)
{
scanf("%d%d%d%d",&x,&fx,&y,&fy);
add(x+n*(fx^1),y+n*fy);
add(y+n*(fy^1),x+n*fx);
}
tot=0;
for (int i=1;i<=n*2;i++)
if (!dfn[i]) tarjan(i);
for (int i=1;i<=n;i++)
if (col[i]==col[i+n]) return printf("IMPOSSIBLE"),0;
printf("POSSIBLE\n");
for (int i=1;i<=n;i++)
if (col[i]>col[i+n]) printf("1 ");
else printf("0 ");
return 0;
}