2-sat
有 $n$ 个变量和 $m$ 条限制,每条限制是 $x_1$ 为 $True/False$ 或 $x_2$ 为 $True/False$,求构造一组可行方案,或者判断无解
sol:
把每个命题拆成原命题和逆否命题,这两个一定等价,要同时满足
然后对于每个限制 $a,b$ , $a$ 向 $否b$ 连有向边,$b$ 向 $否a$ 连有向边
然后 Tarjan 缩点,发现同一个强连通分量里的值一定相等,所以如果 $a$ 和 $否a$ 在同一个强连通分量里就是无解
有解的话可以按 $scc$ 的拓扑序构造一组解,因为 Tarjan 的 $scc$ 标号是拓扑序的逆序,所以每次输出 (id[i]>id[i+n]) 即可
#include <bits/stdc++.h> #define LL long long #define rep(i, s, t) for(register int i = (s), i##end = (t); i <= i##end; ++i) #define dwn(i, s, t) for(register int i = (s), i##end = (t); i >= i##end; --i) using namespace std; inline int read() { int x = 0, f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -f; for(; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0'; return x * f; } const int maxn = 2000010; int n, m; int first[maxn], to[maxn << 1], nx[maxn << 1], cnt; inline void add(int u, int v) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; } int dfn[maxn], low[maxn], st[maxn], _tim, vis[maxn], top; int bl[maxn], scc; void Tarjan(int x) { low[x] = dfn[x] = ++_tim; st[++top] = x; vis[x] = 1; for(int i=first[x];i;i=nx[i]) { if(!dfn[to[i]]) { Tarjan(to[i]); low[x] = min(low[x], low[to[i]]); } else if(vis[to[i]]) low[x] = min(low[x], dfn[to[i]]); } if(low[x] == dfn[x]) { int now = 0; scc++; while(now != x) { now = st[top--]; bl[now] = scc; vis[now] = 0; } } } int main() { n = read(), m = read(); rep(i, 1, m) { int u = read(), a = read(), v = read(), b = read(); add(v + n * (b ^ 1), u + n * a); add(u + n * (a ^ 1), v + n * b); } rep(i, 1, n+n) if(!dfn[i]) Tarjan(i); rep(i, 1, n) if(bl[i] == bl[i + n]) { puts("IMPOSSIBLE"); return 0; } puts("POSSIBLE"); rep(i, 1, n) cout << (bl[i] > bl[i + n]) << " "; cout << endl; }