0%

2-SAT

问题描述:

nn 个 bool 变量,有mm个条件,如 xi\lfloor x_i 为 true/flase 或 xjx_j 为 true/false \rfloor。求是否有方案能满足所有条件。构造出一组符合题意的解。

算法思路:

按照条件建立分层图,令 xix_i 为 true,xi+nx_{i+n} 为 false。

举个例子,设有条件 xi\lfloor x_i 为 flase 或 xjx_j 为 true \rfloor。则当 xix_i 为 true 时,xjx_j 必定为 true。当 xjx_j 为 false时,xix_i 必定为 false。

此时建立有向边 xixjx_i \rightarrow x_jxj+nxi+nx_{j + n} \rightarrow x_{i+n}。当箭头左边条件成立时,右边条件一定成立。但注意,右边条件成立时,左边条件不一定成立。这也是建立有向边而非无向边的原因。

建完所有边后,跑 Tarjan,如果发现 xix_ixi+nx_{i+n} 在同一强连通分量,即条件两两等价,此时矛盾,问题无解。

若有解,xix_ixi+nx_{i+n} 的拓扑序更大的即是 ii 的解。

注意 Tarjan 的 feature,代码实现中解为 coloricolor_i 值更小的点。

模板传送门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <bits/stdc++.h>
#define rei register int
#define N 4000005
using namespace std;
typedef long long LL;
typedef unsigned long long ull;

int n, m, dfn[N], low[N], cnt, co[N], st[N], top, col;
vector<int> g[N];

struct edge
{
int v, nxt;
} e[N];

template <typename T> inline void read(T &x)
{
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -f; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - 48; ch = getchar();}
x *= f;
}

inline void Tarjan(int x)
{
dfn[x] = low[x] = ++cnt;
st[++top] = x;
for (rei i = 0; i < g[x].size(); i++)
{
int v = g[x][i];
if (!dfn[v])
{
Tarjan(v);
low[x] = min(low[x], low[v]);
}
else if (!co[v])
low[x] = min(low[x], dfn[v]);
}
if (low[x] == dfn[x])
{
co[x] = ++col;
while (st[top] != x)
{
co[st[top]] = col;
top--;
}
top--;
}
}

int main()
{
read(n); read(m);
for (rei i = 1; i <= m; i++)
{
int j, a, k, b;
read(j); read(a); read(k); read(b);
g[j + n * (a & 1)].push_back(k + n * (b ^ 1));
g[k + n * (b & 1)].push_back(j + n * (a ^ 1));
}
for (rei i = 1; i <= n * 2; i++)
if (!dfn[i]) Tarjan(i);
for (rei i = 1; i <= n; i++)
{
if (co[i] == co[i + n])
{
printf("IMPOSSIBLE\n");
return 0;
}
}
printf("POSSIBLE\n");
for (rei i = 1; i <= n; i++)
{
int res = co[i] < co[i + n];
printf("%d ", res);
}
return 0;
}