定义 给定 n 个 bool 型变量和 m 个关系式,形如 (x_i) 或(非) (x_j)。求问是否矛盾,不矛盾则需要求出一组解。
解法 建立一个 (2n) 个节点的有向图。第 (2i-1) 与第 (2i) 个节点分别表示 (x_i) 为真与 (x_i) 为假。如果是 (x_i) 或 (x_j) 在有向图中从 (x_i) 假连向 (x_j) 真,并从 (x_j) 假连向 (x_i) 真,表示二者中至少有一个为真,其他情况类似。然后求整个图的强连通分量。如果 (x_i) 假与 (x_i) 真在同一强连通分量中则矛盾。如果不矛盾,则如果 (x_i) 为真所在的强连通分量的拓扑序在 (x_i) 为假的强连通分量前则 (x_i) 为真即可,正确性显然,而拓扑序其实就是 tarjan 算法里面标好的每个强连通分量的前后值,不过是反的罢了。复杂度 (O(n+m))。
#include<cstdio>
#include<vector>
const int maxn = 1E+6 + 5;
int n, m, ind, cnt;
int dfn[maxn], low[maxn];
int top, sta[maxn], scc[maxn];
bool insta[maxn];
std::vector<int> to[maxn];
void DFS(int u) {
dfn[u] = low[u] = ++ind;
sta[++top] = u, insta[u] = 1;
for(auto v : to[u]) {
if(!dfn[v]) DFS(v), low[u] = std::min(low[u], low[v]);
else if(insta[v]) low[u] = std::min(low[u], dfn[v]);
}
if(low[u] == dfn[u]) {
++cnt;
while(top >= 1 && sta[top] != u) {
insta[sta[top]] = 0;
scc[sta[top--]] = cnt;
}
insta[sta[top]] = 0;
scc[sta[top--]] = cnt;
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1, x, y, a, b; i <= m; ++i) {
scanf("%d%d%d%d", &x, &a, &y, &b);
to[2 * x - (a ^ 1)].push_back(y * 2 - b);
to[2 * y - (b ^ 1)].push_back(x * 2 - a);
}
for(int i = 1; i <= n * 2; ++i)
if(!dfn[i]) DFS(i);
for(int i = 1; i <= n; ++i)
if(scc[i * 2 - 1] == scc[i * 2])
return puts("IMPOSSIBLE"), 0;
puts("POSSIBLE");
for(int i = 1; i <= n; ++i)
printf("%d ", scc[i * 2] < scc[i * 2 - 1]);
}