给 n n n 个命题,每个命题只有两个变量,每个变量要么是 1 1 1(真) 要么是 0 0 0(假)。
询问是否有合法的构造使得所有的命题的与成立。
命题条件总结:
连边的方式:
while (m -- )
{
int i, a, j, b;
scanf("%d%d%d%d", &i, &a, &j, &b);
// i,i+n ; j,j+n
add(i + n * (a & 1), j + n * (b ^ 1));
add(j + n * (b & 1), i + n * (a ^ 1));
}
add(2 * i + !a, 2 * j + b);
add(2 * j + !b, 2 * i + a);
因为tarjan得到的id是拓扑逆序,最后正序遍历就行了。
#include
#include
#include
#include
using namespace std;
const int N = 2000010, M = 2000010;
int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], ts, stk[N], top;
int id[N], cnt;
bool ins[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++ ts;
stk[ ++ top] = u, ins[u] = true;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (!dfn[j])
{
tarjan(j);
low[u] = min(low[u], low[j]);
} else if (ins[j]) low[u] = min(low[u], dfn[j]);
}
if (low[u] == dfn[u])
{
int y;
cnt ++ ;
do
{
y = stk[top -- ], ins[y] = false, id[y] = cnt;
} while (y != u);
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int i, a, j, b;
scanf("%d%d%d%d", &i, &a, &j, &b);
// i,i+n ; j,j+n
add(i + n * (a & 1), j + n * (b ^ 1));
add(j + n * (b & 1), i + n * (a ^ 1));
}
for (int i = 1; i <= n * 2; i ++ )
if (!dfn[i])
tarjan(i);
for (int i = 1; i <= n; i ++ )
if (id[i] == id[i + n])
{
puts("IMPOSSIBLE");
return 0;
}
puts("POSSIBLE");
for (int i = 1; i <= n; i ++ )
if (id[i] < id[i + n]) printf("1 ");
else printf("0 ");
return 0;
}