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;
}
View Code
原文地址:https://www.cnblogs.com/Kong-Ruo/p/10668457.html