2-SAT 模板题
题面
题目描述
有 \(n\) 个布尔变量 \(x_1\) ~ \(x_n\),满足 \(m\) 个形如 \(x_i=true/false\) 或 \(x_j=true/false\) 的条件 。
输入
第一行两个整数 \(n\) 和 \(m\),意义如题面所述。接下来的 \(m\) 行,每行四个整数,\(x,a,y,b\) (\(1\leq x,y\leq n\ \ a,b \in \{0,1\}\))。
输出
无解输出 \(IMPOSSIBLE\)。
有解输出 \(POSSIBLE\)。并输出构造出来的解。
采用 \(Special \ Judge\)。
样例
输入
3 1
1 1 3 0
输出
POSSIBLE
0 0 0
题解
连边
- 定义点 \(x\) 表示\((x-(x>n)*n)=(x>n)\),也就是:
- 如果点 \(x\leq n\),则代表第 \(x\) 个数为 \(0\)。
- 如果点 \(x\geq n+1\),则代表第 \(x-n\) 个数为 \(1\)。
- 定义边 (\(x,y\)) 由 \((x-(x>n)*n)=(x>n)\) 可以推出 \((y-(y>n)*n)=(y>n)\)。
对于条件 (\(x=a||y=b\)),我们可以转化为:
\[x=a\ xor\ 1\rightarrow y=b$$$$y=b\ xor\ 1\rightarrow x=a
\]
由此我们就知道了该怎么连边。
add(x + (a ^ 1) * n,y + b * n);
add(y + (b ^ 1) * n,x + a * n);
判无解
于是连完后,我们跑一遍 \(tarjan\),求出其中的强连通分量,如果 \(x\) 和 \(x+n\) 同处于一个强连通分量内,那么显然无解。原因很显然,这意味着我们从 \(x=0\) 推出了 \(x = 1\)。
构造值
接下来是构造值的问题。
我用的是小蓝书的第二种方法,这里就不赘述原理了。
代码
#include<cstdio>
#include<iostream>
using std::min;
const int N = 2e6 + 5;
struct edge {
int next,to;
}a[N << 1];
int head[N],opp[N],n,m,a_size = 1;
inline void add(int u,int v) {
a[++a_size] = (edge){head[u],v};
head[u] = a_size;
}
int dfn[N],low[N],sta[N],c[N],top = 0,cnt = 0,num = 0;
bool ins[N];
void tarjan(int x) {
dfn[x] = low[x] = ++num;
sta[++top] = x; ins[x] = true;
for(int i = head[x]; i; i = a[i].next) {
int y = a[i].to;
if(!dfn[y]) {
tarjan(y);
low[x] = min(low[x],low[y]);
}
else if(ins[y])
low[x] = min(low[x],dfn[y]);
}
if(dfn[x] == low[x]) {
int y; cnt++;
do {
y = sta[top--]; ins[y] = false;
c[y] = cnt;
}while(x != y);
}
}
inline int read() {
int x = 0,flag = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')flag = -1;ch = getchar();}
while(ch >='0' && ch <='9'){x = (x << 3) + (x << 1) + ch - 48;ch = getchar();}
return x * flag;
}
int main() {
n = read(),m = read();
for(int i = 1; i <= m; i++) {
int x = read(),a = read(),y = read(),b = read();
add(x + (a ^ 1) * n,y + b * n);
add(y + (b ^ 1) * n,x + a * n);
} n <<= 1;
for(int i = 1; i <= n; i++)
if(!dfn[i]) tarjan(i);
n >>= 1;
for(int i = 1; i <= n; i++)
if(c[i] == c[i + n]) {puts("IMPOSSIBLE"); return 0;}
puts("POSSIBLE");
for(int i = 1; i <= n; i++)
printf("%d ",c[i] > c[n + i]);
return 0;
}

浙公网安备 33010602011771号