1.2-sat

主要是化简问题,转化成(a1||b1)&&(a2||b2)&&...&&(an||bn)=1,求每个元素的真值。

每个元素真值为真/假抽象成一对点,然后每对里面只能选恰好一个点。除此之外把(ai||bi)==1转化成!ai->bi&&!bi->ai,然后整个图里面A->B的意思就是A选了B就必须选。缩点(同一个强连通分量中的点一定决策一样)之后变成DAG图(同时拿到拓扑序),只要没有成对的点在一个强连通分量里面就存在解;给出一组解的话就是选拓扑序小的那个点。

#include<bits/stdc++.h>

#define maxn 2000010
using namespace std;
int n, m, dfn[maxn], low[maxn], bl[maxn], topt, sum, s[maxn], top, f[maxn];
vector<int> G[maxn];

void Dfs(int u) {
    dfn[u] = low[u] = ++topt;
    s[++top] = u, f[u] = 1;
    for (int v:G[u]) {
        if (dfn[v] == 0) {
            Dfs(v), low[u] = min(low[u], low[v]);
        } else if (f[v])low[u] = min(low[u], dfn[v]);
    }
    if (low[u] == dfn[u]) {
        sum++;
        while (s[top] != u) {
            f[s[top]] = 0, bl[s[top]] = sum, top--;
        }
        f[s[top]] = 0, bl[s[top]] = sum, top--;
    }
}

int main() {
    scanf("%d%d", &n, &m);
    int a, va, b, vb;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d%d", &a, &va, &b, &vb);
        G[a + n * (va & 1)].push_back(b + n * (vb ^ 1));
        G[b + n * (vb & 1)].push_back(a + n * (va ^ 1));
    }
    for (int i = 1; i <= n * 2; i++)
        if (!dfn[i])Dfs(i);
    for (int i = 1; i <= n*2; i++)printf("%d ", bl[i]);
    printf("\n");
    int ok = 1;
    for (int i = 1; i <= n; i++)
        if (bl[i] == bl[i + n])ok = 0;
    if (ok == 0)printf("IMPOSSIBLE\n");
    else {
        printf("POSSIBLE\n");
        for (int i = 1; i <= n; i++)
            if (bl[i] < bl[i + n])printf("1 ");
            else printf("0 ");
        printf("\n");
    }
    return 0;
}