# ---------- Algoritmo para resolver 2-SAT ----------
# Usa el método de "grafo de implicaciones" + componentes fuertemente conexas (Tarjan)
# Cada variable x_i tiene dos nodos:
# i -> representa x_i
# i + n -> representa ¬x_i
#
# Si una variable y su negación están en la misma SCC => no hay solución.
# Si no, se asignan según el orden inverso de las SCC.
# ---------- Entrada ----------
n, m = map(int, input("Ingrese n (variables) y m (cláusulas): ").split())
# Creamos el grafo de 2n nodos
N = 2 * n
out = [[] for _ in range(N)]
inv = [[] for _ in range(N)] # grafo inverso para Kosaraju
# Función auxiliar para obtener el índice de una variable
# Ejemplo: x3 => 2
# ¬x3 => 2 + n
def var_index(x):
# x viene como entero positivo o negativo
v = abs(x) - 1
return v if x > 0 else v + n
# ---------- Construcción del grafo de implicaciones ----------
for _ in range(m):
a, b = map(int, input().split())
# cláusula (a ∨ b) -> (¬a → b) y (¬b → a)
out[var_index(-a)].append(var_index(b))
out[var_index(-b)].append(var_index(a))
inv[var_index(b)].append(var_index(-a))
inv[var_index(a)].append(var_index(-b))
# ---------- Kosaraju para SCC ----------
marked = [False] * N
order = []
comp = [-1] * N
def dfs1(v):
marked[v] = True
for u in out[v]:
if not marked[u]:
dfs1(u)
order.append(v)
def dfs2(v, c):
comp[v] = c
for u in inv[v]:
if comp[u] == -1:
dfs2(u, c)
# Primer paso: orden topológico por postorden
for v in range(N):
if not marked[v]:
dfs1(v)
# Segundo paso: recorrer grafo inverso en orden inverso
c = 0
for v in reversed(order):
if comp[v] == -1:
dfs2(v, c)
c += 1
# ---------- Verificación de satisfacibilidad ----------
satisfacible = True
for i in range(n):
if comp[i] == comp[i + n]:
satisfacible = False
break
if not satisfacible:
print("La fórmula NO es satisfacible.")
else:
print("La fórmula es satisfacible.")
# Determinar asignación
assignment = [False] * n
for i in range(n):
assignment[i] = comp[i] > comp[i + n]
print("Asignación posible (x1..xn):")
print(" ".join("1" if val else "0" for val in assignment))