# Algoritmo para encontrar puentes en un grafo no dirigido usando DFS (Tarjan)
# Un "puente" es una arista cuya eliminación incrementa el número de componentes conexas.
# ---------- Entrada y construcción del grafo ----------
n, m = map(int, input("Ingrese n (nodos) y m (aristas): ").split())
# Lista de adyacencia (indexada desde 1)
out = [[] for _ in range(n + 1)]
for _ in range(m):
a, b = map(int, input().split())
out[a].append(b)
out[b].append(a) # grafo no dirigido
# ---------- Inicialización de estructuras ----------
marked = [False] * (n + 1) # nodos visitados
depth = [0] * (n + 1) # profundidad del nodo en el árbol DFS
up = [0] * (n + 1) # menor profundidad alcanzable desde el subárbol de v
# ---------- DFS para detectar puentes ----------
def dfs(v, p):
marked[v] = True
up[v] = depth[v] # al menos puede llegar a sí mismo
for u in out[v]:
if u == p:
continue # no volver por la misma arista
elif marked[u]:
# Si u ya fue visitado, es una arista de retroceso
up[v] = min(up[v], depth[u])
else:
# Explorar hijo u
depth[u] = depth[v] + 1
dfs(u, v)
up[v] = min(up[v], up[u])
# Si el valor mínimo alcanzable desde v es mayor
# que la profundidad de su padre, (p,v) es un puente
# Esto quiere decir que ninguna backedge llega al nivel del p o a ancestros mas viejos
if p != -1 and up[v] > depth[p]:
print(f"{p} -> {v} es un puente")
# ---------- Ejecutar DFS (en caso de grafo no conexo, revisar todos los nodos) ----------
for i in range(1, n + 1):
if not marked[i]:
depth[i] = 0
dfs(i, -1) #
IyBBbGdvcml0bW8gcGFyYSBlbmNvbnRyYXIgcHVlbnRlcyBlbiB1biBncmFmbyBubyBkaXJpZ2lkbyB1c2FuZG8gREZTIChUYXJqYW4pCiMgVW4gInB1ZW50ZSIgZXMgdW5hIGFyaXN0YSBjdXlhIGVsaW1pbmFjacOzbiBpbmNyZW1lbnRhIGVsIG7Dum1lcm8gZGUgY29tcG9uZW50ZXMgY29uZXhhcy4KCiMgLS0tLS0tLS0tLSBFbnRyYWRhIHkgY29uc3RydWNjacOzbiBkZWwgZ3JhZm8gLS0tLS0tLS0tLQpuLCBtID0gbWFwKGludCwgaW5wdXQoIkluZ3Jlc2UgbiAobm9kb3MpIHkgbSAoYXJpc3Rhcyk6ICIpLnNwbGl0KCkpCgojIExpc3RhIGRlIGFkeWFjZW5jaWEgKGluZGV4YWRhIGRlc2RlIDEpCm91dCA9IFtbXSBmb3IgXyBpbiByYW5nZShuICsgMSldCgpmb3IgXyBpbiByYW5nZShtKToKICAgIGEsIGIgPSBtYXAoaW50LCBpbnB1dCgpLnNwbGl0KCkpCiAgICBvdXRbYV0uYXBwZW5kKGIpCiAgICBvdXRbYl0uYXBwZW5kKGEpICAjIGdyYWZvIG5vIGRpcmlnaWRvCgojIC0tLS0tLS0tLS0gSW5pY2lhbGl6YWNpw7NuIGRlIGVzdHJ1Y3R1cmFzIC0tLS0tLS0tLS0KbWFya2VkID0gW0ZhbHNlXSAqIChuICsgMSkgICMgbm9kb3MgdmlzaXRhZG9zCmRlcHRoID0gWzBdICogKG4gKyAxKSAgICAgICAjIHByb2Z1bmRpZGFkIGRlbCBub2RvIGVuIGVsIMOhcmJvbCBERlMKdXAgPSBbMF0gKiAobiArIDEpICAgICAgICAgICMgbWVub3IgcHJvZnVuZGlkYWQgYWxjYW56YWJsZSBkZXNkZSBlbCBzdWLDoXJib2wgZGUgdgoKIyAtLS0tLS0tLS0tIERGUyBwYXJhIGRldGVjdGFyIHB1ZW50ZXMgLS0tLS0tLS0tLQpkZWYgZGZzKHYsIHApOgogICAgbWFya2VkW3ZdID0gVHJ1ZQogICAgdXBbdl0gPSBkZXB0aFt2XSAgIyBhbCBtZW5vcyBwdWVkZSBsbGVnYXIgYSBzw60gbWlzbW8KICAgIAogICAgZm9yIHUgaW4gb3V0W3ZdOgogICAgICAgIGlmIHUgPT0gcDoKICAgICAgICAgICAgY29udGludWUgICMgbm8gdm9sdmVyIHBvciBsYSBtaXNtYSBhcmlzdGEKICAgICAgICBlbGlmIG1hcmtlZFt1XToKICAgICAgICAgICAgIyBTaSB1IHlhIGZ1ZSB2aXNpdGFkbywgZXMgdW5hIGFyaXN0YSBkZSByZXRyb2Nlc28KICAgICAgICAgICAgdXBbdl0gPSBtaW4odXBbdl0sIGRlcHRoW3VdKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgICMgRXhwbG9yYXIgaGlqbyB1CiAgICAgICAgICAgIGRlcHRoW3VdID0gZGVwdGhbdl0gKyAxCiAgICAgICAgICAgIGRmcyh1LCB2KQogICAgICAgICAgICB1cFt2XSA9IG1pbih1cFt2XSwgdXBbdV0pCiAgICAKICAgICMgU2kgZWwgdmFsb3IgbcOtbmltbyBhbGNhbnphYmxlIGRlc2RlIHYgZXMgbWF5b3IKICAgICMgcXVlIGxhIHByb2Z1bmRpZGFkIGRlIHN1IHBhZHJlLCAocCx2KSBlcyB1biBwdWVudGUKICAgICMgRXN0byBxdWllcmUgZGVjaXIgcXVlIG5pbmd1bmEgYmFja2VkZ2UgbGxlZ2EgYWwgbml2ZWwgZGVsIHAgbyBhIGFuY2VzdHJvcyBtYXMgdmllam9zCiAgICBpZiBwICE9IC0xIGFuZCB1cFt2XSA+IGRlcHRoW3BdOgogICAgICAgIHByaW50KGYie3B9IC0+IHt2fSBlcyB1biBwdWVudGUiKQoKIyAtLS0tLS0tLS0tIEVqZWN1dGFyIERGUyAoZW4gY2FzbyBkZSBncmFmbyBubyBjb25leG8sIHJldmlzYXIgdG9kb3MgbG9zIG5vZG9zKSAtLS0tLS0tLS0tCmZvciBpIGluIHJhbmdlKDEsIG4gKyAxKToKICAgIGlmIG5vdCBtYXJrZWRbaV06CiAgICAgICAgZGVwdGhbaV0gPSAwCiAgICAgICAgZGZzKGksIC0xKSAjIAo=