(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 ,mleq 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]
", 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
");
else {
printf("POSSIBLE
");
for(int i = 1; i <= n; i++)
printf("%d%c", bel[i] < bel[i + n]? 1 : 0, i == n? '
' : ' ');
}
return 0;
}