P4782 【模板】2-SAT 问题
\(\color{#0066ff}{ 题目描述 }\)
有\(n\)个布尔变量\(x_1\)~\(x_n\),另有\(m\)个需要满足的条件,每个条件的形式都是“\(x_i\)为\(true/false\)或\(x_j\)为\(true/false\)”。比如“\(x_1\)为真或\(x_3\)为假”、“\(x_7\)为假或\(x_2\)为假”。2-SAT 问题的目标是给每个变量赋值使得所有条件得到满足。
$\color{#0066ff}{ 输入格式 } $
第一行两个整数n和m,意义如体面所述。
接下来m行每行4个整数 i a j b,表示“\(x_i\)为a或\(x_j\)为b”(a,b∈{0,1})
\(\color{#0066ff}{输出格式}\)
如无解,输出“IMPOSSIBLE”(不带引号); 否则输出"POSSIBLE"(不带引号),下 一行n个整数\(x_1\)~\(x_n\)(\(x_i\)∈{0,1}),表示构造出的解。
\(\color{#0066ff}{输入样例}\)
3 1
1 1 3 0
\(\color{#0066ff}{输出样例}\)
POSSIBLE
0 0 0
\(\color{#0066ff}{数据范围与提示}\)
有spj
\(n ,m\leq 10^6\)
\(\color{#0066ff}{ 题解 }\)
2——SAT板子题
建图
每个变量x拆成x和x+n
x代表x取true的状态,x+n代表x取false的状态
图中有向边的含义:\(x->y\)有状态x, 必有状态y
比如\(x|y=1\)也就是说x和y至少有一个是1
就这样连\(x+n->y, y+n->x\)
x为0时,要保证式子成立,y必须为1
这样就能建好图了
考虑一个环,一个知道其中一个点的状态,其它的都出来了,所以锁点一下
当x和x+n在一个强联通分量里就不行了,冲突了,无解
现在考虑怎么输出方案
当 \(x\) 所在的强连通分量的拓扑序在 \(x+n\) 所在的强连通分量的拓扑序之后取 \(x\) 为真即可
在tarjan的过程中,实际上我们强联通分量的编号就是倒着的拓扑序qwq
Code
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 2e6 + 100;
struct node {
int to;
node *nxt;
node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
void *operator new(size_t) {
static node *S = NULL, *T = NULL;
return (S == T) && (T = (S = new node[1024]) + 1024), S++;
}
};
node *head[maxn];
bool ins[maxn];
int st[maxn], top, bel[maxn], cnt, n, m, tot;
int dfn[maxn], low[maxn];
void add(int from, int to) {
head[from] = new node(to, head[from]);
}
void tarjan(int x) {
dfn[x] = low[x] = ++tot;
ins[st[++top] = x] = true;
for(node *i = head[x]; i; i = i->nxt) {
if(!dfn[i->to]) {
tarjan(i->to);
low[x] = std::min(low[x], low[i->to]);
}
else if(ins[i->to]) low[x] = std::min(low[x], dfn[i->to]);
}
if(dfn[x] == low[x]) {
cnt++;
do {
bel[st[top]] = cnt;
ins[st[top]] = false;
top--;
}while(st[top + 1] != x);
}
}
bool judge() {
for(int i = 1; i <= n; i++) printf("i = %d, bel = [%d,%d]\n", i, bel[i], bel[i + n]);
for(int i = 1; i <= n; i++) if(bel[i] == bel[i + n]) return true;
return false;
}
int main() {
n = in(), m = in();
int x, y, xx, yy;
for(int i = 1; i <= m; i++) {
x = in(), xx = in(), y = in(), yy = in();
if(xx && yy) add(x + n, y), add(y + n, x);
if(xx && !yy) add(x + n, y + n), add(y, x);
if(!xx && yy) add(x, y), add(y + n, x + n);
if(!xx && !yy) add(x, y + n), add(y, x + n);
}
for(int i = 1; i <= n << 1; i++) if(!dfn[i]) tarjan(i);
if(judge()) printf("IMPOSSIBLE\n");
else {
printf("POSSIBLE\n");
for(int i = 1; i <= n; i++)
printf("%d%c", bel[i] < bel[i + n]? 1 : 0, i == n? '\n' : ' ');
}
return 0;
}
----olinr