Problem:


Score : 300 points

Problem Statement

You are given an undirected connected graph with N vertices and M edges that does not contain self-loops and double edges.

The i-th edge (1 \leq i \leq M) connects Vertex a_i and Vertex b_i.  

An edge whose removal disconnects the graph is called a bridge.

Find the number of the edges that are bridges among the M edges.  

Notes

A self-loop is an edge i such that a_i=b_i (1 \leq i \leq M).

Double edges are a pair of edges i,j such that a_i=a_j and b_i=b_j (1 \leq i<j \leq M).

An undirected graph is said to be connected when there exists a path between every pair of vertices.

Constraints

2 \leq N \leq 50

N-1 \leq M \leq min(N(N−1)⁄2,50)

1 \leq a_i<b_i \leq N

The given graph does not contain self-loops and double edges.

The given graph is connected.

Input

Input is given from Standard Input in the following format:  

N M  
a_1 b_1  
a_2 b_2
:  
a_M b_M

Output

Print the number of the edges that are bridges among the M edges.

Sample Input 1

7 7
1 3
2 7
3 4
4 5
4 6
5 6
6 7

Sample Output 1

4

The figure below shows the given graph:

The edges shown in red are bridges. There are four of them.

Sample Input 2

3 3
1 2
1 3
2 3

Sample Output 2

0

It is possible that there is no bridge.

Sample Input 3

6 5
1 2
2 3
3 4
4 5
5 6

Sample Output 3

5

It is possible that every edge is a bridge.



Sample input:
7 7
1 3
2 7
3 4
4 5
4 6
5 6
6 7


Sample output:
4

Submission status: Accepted

Solution code:
class UnionFind:
  def __init__(self, n):
    self.p = [-1]*n
    # union by rank
    self.r = [1]*n
 
  def find(self, x):
    if self.p[x] < 0:
      return x
    else:
      self.p[x] = self.find(self.p[x])
      return self.p[x]

  def union(self, x, y):
    rx, ry = self.find(x), self.find(y)

    if rx != ry:
      if self.r[rx] > self.r[ry]:
        rx, ry = ry, rx
      if self.r[rx] == self.r[ry]:
        self.r[ry] += 1
      self.p[ry] += self.p[rx]
      self.p[rx] = ry

  def same(self, x, y):
    return self.find(x) == self.find(y)

  def count_member(self, x):
    return -self.p[self.find(x)]

n,m=map(int,input().split())
ab=[]
for i in range(m):
  a,b=map(int,input().split())
  ab.append((a,b))
ans=0
for i in range(m):
  uf=UnionFind(n+1)
  connection=0
  for j in range(m):
    if i==j:continue
    a,b=ab[j]
    uf.union(a,b)
  connection=uf.count_member(1)
  if connection!=n:ans+=1
print(ans)

Submission status: Accepted

Solution code:
from copy import deepcopy
from collections import deque
n, m = map(int, input().split())
G = [[] for _ in range(n)]
V = []
for i in range(m):
    a, b = list(map(lambda x: x-1, map(int, input().split())))
    G[a].append(b)
    G[b].append(a)
    V.append([a,b])

ans = 0
# 各辺について、除いた場合グラフが非連結になるか確かめる.
# 非連結ならば、適当な頂点から探索した時に到達できない頂点が存在する.
for i in range(m):
    _G = deepcopy(G)
    _G[V[i][0]].remove(V[i][1])
    _G[V[i][1]].remove(V[i][0])
    # BFS
    ## キューの初期化
    que = deque([0])
    ## 距離の初期化
    D = [-1]*n
    D[0] = 0
    ##キューが尽きるまで探索を行う
    while que:
        ### 現在地を取得.
        nw = que.popleft()
        ### 現在地から行けるところについて探索.
        for nx in _G[nw]:
            if D[nx]!=-1: continue
            D[nx] = D[nw]+1
            que.append(nx)
    if min(D) == -1:
        ans += 1
print(ans)

Submission status: Accepted

Solution code:
n,m = map(int,input().split())
g = [[]*(n+1) for _ in range(n+1)]
d = []
for _ in range(m):
    a,b = map(int,input().split())
    g[a].append(b)
    g[b].append(a)
    d.append([a,b])
seen = [False]*(n+1)
seen[0]=True
def dfs(s):
    seen[s]=True
    for e in g[s]:
        if not seen[e]:
            dfs(e)
cnt = 0
for i in range(m):
    a,b = d[i]
    g[a].remove(b)
    g[b].remove(a)
    dfs(1)
    for i in range(n+1):
        if not seen[i]:
            cnt += 1
            break
    seen = [False]*(n+1)
    seen[0]=True
    g[a].append(b)
    g[b].append(a)
print(cnt)

Submission status: Accepted

Solution code:
from collections import deque
N, M = map(int, input().split())
ab = [list(map(int, input().split())) for _ in range(M)]
c = 0

for i in range(M):
    G = [[] for _ in range(N)]
    for j in range(M):
        if i == j:
            continue
        a, b = ab[j][0], ab[j][1]
        G[a - 1].append(b - 1)
        G[b - 1].append(a - 1)
    Q = deque()
    Q.append(0)
    d = [0] * N
    d[0] = 1
    while Q:
        q = Q.popleft()
        for g in G[q]:
            if d[g] == 0:
                d[g] = 1
                Q.append(g)
    if 0 in d:
        c += 1

print(c)

Submission status: Accepted

Solution code:
import sys
input = sys.stdin.buffer.readline
sys.setrecursionlimit(10**9)
from collections import deque, defaultdict
n , m = map(int, input().split())
# n : 頂点の数 m : 辺の数
#隣接リスト
#G = defaultdict(lambda:[])
li = []
#################################
for _ in range(m):
    a, b = map(int, input().split())
    a, b = a-1, b-1
    li.append((a, b))
    
#################################

#幅優先探索でトポロジカル順序を求める
#頂点iからの幅優先探索
def bfs(i):
    parent = [-10]*n #parent[i] : 頂点iの親
    order = []
    parent[i] = -1
    stack = deque()
    u = i
    stack.append(u)
    while stack:
        x = stack.popleft()
        order.append(x)
        for v in G[x]:
            if v == parent[x]:
                continue
            if parent[v] != -10:
                continue
            parent[v] = x
            stack.append(v)
    return parent
            
ans = 0
for i in range(m):
    G = [[] for _ in range(n)]
    for j in range(m):
        if i == j:
            continue
        a, b = li[j]
        G[a].append(b)
        G[b].append(a)
    li_p = bfs(0)
    for k in li_p:
        if k == -10:
            ans += 1
            break
print(ans)

Submission status: Accepted

Solution code:
from itertools import combinations
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.root = [-1]*(n+1)
        self.rnk = [0]*(n+1)
    def Find_Root(self, x):
        if(self.root[x] < 0):
            return x
        else:
            self.root[x] = self.Find_Root(self.root[x])
            return self.root[x]
    def Unite(self, x, y):
        x = self.Find_Root(x)
        y = self.Find_Root(y)
        if(x == y):
            return 
        elif(self.rnk[x] > self.rnk[y]):
            self.root[x] += self.root[y]
            self.root[y] = x
        else:
            self.root[y] += self.root[x]
            self.root[x] = y
            if(self.rnk[x] == self.rnk[y]):
                self.rnk[y] += 1
    def isSameGroup(self, x, y):
        return self.Find_Root(x) == self.Find_Root(y)
    def Count(self, x):
        return -self.root[self.Find_Root(x)]

n,m=map(int,input().split())
uf=UnionFind(n)
e=[]
comb=list(combinations(list(range(n)),2))
for _ in range(m):
  a,b=map(int,input().split())
  e.append((a-1,b-1))
ans=0
for i in range(m):
  uf=UnionFind(n)
  for j in range(m):
    if i==j:continue
    uf.Unite(*e[j])
  if not all([uf.isSameGroup(x,y) for x,y in comb]):ans+=1
print(ans)

Submission status: Accepted

Solution code:
n,m=map(int,input().split())
all_edge=[]
for i in range(m):
    a,b=map(int,input().split())
    all_edge.append([a-1,b-1])

def dfs(lst):
    visited=[None]*n
    visited[0]=1
    stack=[]
    for i in lst[0]:
        stack.append(i)
    while stack:
        new=stack.pop()
        if visited[new]==None:
            visited[new]=1
            for i in lst[new]:
                stack.append(i)
    if None in visited:
        return True
    else:
        return False

ans=0
for i in range(m): #橋の回数
    edge=[[] for _ in range(n)]
    for j in range(m): #1個除外する
        if i==j:
            continue
        a=all_edge[j][0]
        b=all_edge[j][1]
        edge[a].append(b)
        edge[b].append(a)
    if dfs(edge):
        ans+=1
print(ans)

Submission status: Accepted

Solution code:
# xの根を求める
def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x])
        return par[x]


# xとyの属する集合の併合
def union(x, y):
    x = find(x)
    y = find(y)
    
    if x == y:
        return

    # sizeの大きいほうがx
    if par[x] > par[y]:
        x, y = y, x

    par[x] += par[y]
    par[y] = x


# xが属する集合の個数
def size(x):
    return -par[find(x)]


# xとyが同じ集合に属するかの判定
def same(x, y):
    return find(x) == find(y)


n, m = map(int, input().split())
e = [list(map(int, input().split())) for _ in range(m)]

res = 0
for i in range(m):
    par = [-1] * n
    flag = False

    for j in range(m):
        if i == j:
            continue
        a, b = e[j]
        union(a-1, b-1)
    
    for j in range(n):
        for k in range(j+1, n):
            if not same(j, k):
                flag = True
    
    if flag:
        res += 1

print(res)

Submission status: Accepted

Solution code:
from collections import deque

n, m = map(int, input().split())
e = [list(map(int, input().split())) for _ in range(m)]

res = 0
for i in range(m):
    edge = [[] for _ in range(n)]
    for j in range(m):
        if i == j:
            continue
        a, b = e[j]
        edge[a-1].append(b-1)
        edge[b-1].append(a-1)
    
    flag = False
    for j in range(n):
        d = deque([j])
        visited = [False] * n
        visit_cnt = 0
        while d:
            node = d.popleft()
            visited[node] = True
            visit_cnt += 1
            for next_node in edge[node]:
                if not visited[next_node]:
                    d.append(next_node)
        if visit_cnt < n:
            flag = True
            break
    
    if flag:
        res += 1

print(res)

Submission status: Accepted

Solution code:
N, M = map(int, input().split())
ab = [[] for _ in range(N)]
abl = []
for _ in range(M):
    a, b = map(int, input().split())
    ab[a-1].append(b-1)
    ab[b-1].append(a-1)
    abl.append([a-1, b-1])


# DFS
def dfs(v):
    global da, db
    if tf[v]:
        return
    tf[v] = True
    for nv in ab[v]:
        if v == da and nv == db:
            continue
        if v == db and nv == da:
            continue
        dfs(nv)
    return


# 辺を１本ずつ抜いていってそれぞれDFS
ans = 0
for i in range(M):
    da, db = abl[i]
    tf = [False for _ in range(N)]
    dfs(0)
    if not all(e for e in tf):
        ans += 1
print(ans)

Submission status: Accepted

Solution code:
N, M = list(map(int,input().split()))
in_ = []
for i in range(M):
    a, b = map(int, input().split())
    in_.append([a, b])
    
class UnionFind():
    
    # 要素数を指定して作成 はじめは全ての要素が別グループに属し、親はいない
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
    
    # xの根を返す
    def find(self, x):
        if self.parents[x] < 0:  return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
    
    # xとyが属するグループを併合
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:  return

        if self.parents[x] > self.parents[y]:  x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x
    
    # xが属するグループの要素数
    def size(self, x):  return -self.parents[self.find(x)]
    
    # ｘとyが同じグループか否か
    def same(self, x, y):  return self.find(x) == self.find(y)
    
    # xと同じメンバーの要素
    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]
    
    # 根の一覧
    def roots(self):  return [i for i, x in enumerate(self.parents) if x < 0]
    
    # グループ数
    def group_count(self):  return len(self.roots())
    
    # 可視化 [print(uf)]
    def __str__(self):  return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
        
ans = 0
for i in range(M):
    uf = UnionFind(N)
    for j in range(M):
        if i == j:  continue
        uf.union(in_[j][0] - 1, in_[j][1] - 1)
    ans += (uf.group_count() != 1)
print(ans)

Submission status: Accepted

Solution code:
N, M = list(map(int,input().split()))
graph = [[-1 for i in range(N)] for i in range(N)]
in_list = []
for i in range(M):
    a, b = list(map(int,input().split()))
    in_list.append([a, b])
    graph[a - 1][b - 1] = 1
    graph[b - 1][a - 1] = 1
    
# 頂点 node を含む連結成分が木なら 1 を返す
def dfs(node, parent):
    ret = 1
    global seen
    if seen[node]:  return 0
    seen[node] = True
    for i in range(N):
        if graph[node][i] == 1 and i != parent:
            ret = min(dfs(i, node), ret)
    return ret

ans = 0

for i in range(M):
    
    graph[in_list[i][0] - 1][in_list[i][1] - 1] = -1
    graph[in_list[i][1] - 1][in_list[i][0] - 1] = -1
    
    seen = [False for _ in range(N)]
    link = 0 # 連結成分
    wood = 0 # 木の個数
    
    for j in range(N):
        if seen[j] == False:
            link += 1
            wood += dfs(j, 0)
            seen[j] = True
            
    ans += (link != 1)
    
    graph[in_list[i][0] - 1][in_list[i][1] - 1] = 1
    graph[in_list[i][1] - 1][in_list[i][0] - 1] = 1
    
print(ans)

Submission status: Accepted

Solution code:
n, m = map(int,input().split())
ikeru = [[] for _ in range(n)]
hashi = []
for i in range(m):
	a, b = map(int, input().split())
	ikeru[a-1].append(b-1)
	ikeru[b-1].append(a-1)
	hashi.append((a-1, b-1))

ans = 0
for num in hashi:
	settansaku = set([])
	setmada = {0}
	kouho = 1
	
	while kouho != 0:
		for i in list(setmada):
			settansaku.add(i)
			setmada.remove(i)
			kouho -= 1
			for k in ikeru[i]:
				if not k in setmada:
					if not k in settansaku:
						if (k, i) != num and (i, k) != num:
							setmada.add(k)
							kouho += 1
	
	if len(settansaku) != n:
		ans += 1

print(ans)

Submission status: Accepted

Solution code:
#####################################################################################################
##### Low Link (橋、関節点)
#####################################################################################################

"""

計算量
O(V+E)

参考
https://algo-logic.info/bridge-lowlink/


"""



import sys
input = sys.stdin.readline
import marshal


class Graph:
    def __init__(self, n, dictated=False, decrement=True, destroy=False, edges=[]):
        self.n = n
        self.dictated = dictated
        self.decrement = decrement
        self.destroy = destroy
        self.edges = [set() for _ in range(self.n)]
        self.parent = [-1]*self.n
        for x, y in edges:
            self.add_edge(x,y)

    def add_edge(self, x, y):
        if self.decrement:
            x -= 1
            y -= 1
        self.edges[x].add(y)
        if self.dictated == False:
            self.edges[y].add(x)

    def add_adjacent_list(self, i, adjacent_list):
        if self.decrement:
            self.edges[i] = set(map(lambda x: x - 1, adjacent_list))
        else:
            self.edges[i] = set(adjacent_list)

    def bridge_detector(self, articular_list=False):
        """
        :param p: スタート地点
        :param save: True = 前回の探索結果を保持する
        :param articular_list: True = 関節点のリストを返す
        :return: 各点までの距離と何番目に発見したかを返す
        """

        if self.destroy:
            edge2 = self.edges
        else:
            edge2 = marshal.loads(marshal.dumps(self.edges))

        start, time = 0, 0
        p, t = start, time
        self.parent[p] = -2
        ord = [-1]*self.n       # ord = DFS で頂点 q を何番目に探索したか
        ord[p] = t
        low = [-1]*self.n       # low = q からの 0 回以上木辺を使って前進し、後退辺を1回以下使って後退する事で到達できる頂点 p について、ord[p] の最小値
        low[p] = 0
        articular = []          # 関節点の集合
        bridge = []             # 橋の集合

        if len(edge2[p]) >= 2:
            """ 根に子が二つ異常存在する場合は、根も関節点 """
            articular.append(p)

        while True:
            if edge2[p]:
                q = edge2[p].pop()
                if q == self.parent[p] and not self.dictated:
                    """ 逆流した時の処理 """
                    """"""""""""""""""""
                    continue
                if self.parent[q] != -1:
                    """ サイクルで同一点を訪れた時の処理 """
                    if ord[q] < low[p]:
                        low[p] = ord[q]
                    """"""""""""""""""""
                    continue
                """ p から q への引継ぎ"""
                """"""""""""""""""""
                ord[q] = low[q] = t + 1
                self.parent[q] = p
                p, t = q, t + 1
            else:
                if p == start and t == time:
                    break
                """ p から進める点がもう無い時の点 p における処理 """
                parent_p = self.parent[p]
                if low[p] < low[parent_p]:
                    low[parent_p] = low[p]
                """ この時点でlow[p]が確定するので、ord[parent[p]] ≤ low[p] を判定しておく"""
                if ord[parent_p] <= low[p]:
                    if ord[parent_p]:
                        articular.append(parent_p + self.decrement)
                    if ord[parent_p] < low[p]:
                        bridge.append((parent_p + self.decrement, p + self.decrement))
                """"""""""""""""""""
                p, t = self.parent[p], t - 1
                """ 点 p から親ノードに戻ってきた時の親ノードにおける処理 """
                """"""""""""""""""""
        if articular_list == False:
            return bridge
        else:
            return articular

    def draw(self):
        """
        :return: グラフを可視化
        """
        import matplotlib.pyplot as plt
        import networkx as nx

        if self.dictated:
            G = nx.DiGraph()
        else:
            G = nx.Graph()
        for x in range(self.n):
            for y in self.edges[x]:
                G.add_edge(x + self.decrement, y + self.decrement)

        nx.draw_networkx(G)
        plt.show()


def inc(A):
    return list(map(lambda x: x+1, A))


def dec(A):
    return list(map(lambda x: x-1, A))

###############################################################################################

N, M = map(int, input().split())
graph = Graph(N, dictated=False, decrement=True, destroy=False)
for _ in range(M):
    x, y = map(int, input().split())
    graph.add_edge(x, y)



print(len(graph.bridge_detector(articular_list=False)))

# graph.draw()

"""
9 10
7 8
8 9
3 6
9 2
1 2
2 3
3 4
4 5
5 6
6 7

"""

Submission status: Accepted

Solution code:
#####################################################################################################
##### Low Link (橋、関節点)
#####################################################################################################

"""

計算量
O(V+E)

参考
https://algo-logic.info/bridge-lowlink/


"""



import sys
input = sys.stdin.readline
import marshal


class Graph:
    def __init__(self, n, dictated=False, decrement=True, destroy=False, edges=[]):
        self.n = n
        self.dictated = dictated
        self.decrement = decrement
        self.destroy = destroy
        self.edges = [set() for _ in range(self.n)]
        self.parent = [-1]*self.n
        for x, y in edges:
            self.add_edge(x,y)

    def add_edge(self, x, y):
        if self.decrement:
            x -= 1
            y -= 1
        self.edges[x].add(y)
        if self.dictated == False:
            self.edges[y].add(x)

    def add_adjacent_list(self, i, adjacent_list):
        if self.decrement:
            self.edges[i] = set(map(lambda x: x - 1, adjacent_list))
        else:
            self.edges[i] = set(adjacent_list)

    def bridge_detector(self, articular_list=False):
        """
        :param p: スタート地点
        :param save: True = 前回の探索結果を保持する
        :param articular_list: True = 関節点のリストを返す
        :return: 各点までの距離と何番目に発見したかを返す
        """


        start, time = 0, 0
        p, t = start, time
        self.parent[p] = -2
        ord = [-1]*self.n       # ord = DFS で頂点 q を何番目に探索したか
        ord[p] = t
        low = [-1]*self.n       # low = q からの 0 回以上木辺を使って前進し、後退辺を1回以下使って後退する事で到達できる頂点 p について、ord[p] の最小値
        low[p] = 0
        articular = []          # 関節点の集合
        bridge = []             # 橋の集合

        if len(self.edges[p]) >= 2:
            """ 根に子が二つ異常存在する場合は、根も関節点 """
            articular.append(p)

        while True:
            if self.edges[p]:
                q = self.edges[p].pop()
                if q == self.parent[p] and not self.dictated:
                    """ 逆流した時の処理 """
                    """"""""""""""""""""
                    continue
                if self.parent[q] != -1:
                    """ サイクルで同一点を訪れた時の処理 """
                    if ord[q] < low[p]:
                        low[p] = ord[q]
                    """"""""""""""""""""
                    continue
                """ p から q への引継ぎ"""
                """"""""""""""""""""
                ord[q] = low[q] = t + 1
                self.parent[q] = p
                p, t = q, t + 1
            else:
                if p == start and t == time:
                    break
                """ p から進める点がもう無い時の点 p における処理 """
                parent_p = self.parent[p]
                if low[p] < low[parent_p]:
                    low[parent_p] = low[p]
                """ この時点でlow[p]が確定するので、ord[parent[p]] ≤ low[p] を判定しておく"""
                if ord[parent_p] <= low[p]:
                    if ord[parent_p]:
                        articular.append(parent_p + self.decrement)
                    if ord[parent_p] < low[p]:
                        bridge.append((parent_p + self.decrement, p + self.decrement))
                """"""""""""""""""""
                p, t = self.parent[p], t - 1
                """ 点 p から親ノードに戻ってきた時の親ノードにおける処理 """
                """"""""""""""""""""
        if articular_list == False:
            return bridge
        else:
            return articular

    def draw(self):
        """
        :return: グラフを可視化
        """
        import matplotlib.pyplot as plt
        import networkx as nx

        if self.dictated:
            G = nx.DiGraph()
        else:
            G = nx.Graph()
        for x in range(self.n):
            for y in self.edges[x]:
                G.add_edge(x + self.decrement, y + self.decrement)

        nx.draw_networkx(G)
        plt.show()


def inc(A):
    return list(map(lambda x: x+1, A))


def dec(A):
    return list(map(lambda x: x-1, A))

###############################################################################################

N, M = map(int, input().split())
graph = Graph(N, dictated=False, decrement=True, destroy=True)
for _ in range(M):
    x, y = map(int, input().split())
    graph.add_edge(x, y)



print(len(graph.bridge_detector(articular_list=False)))

# graph.draw()

"""
9 10
7 8
8 9
3 6
9 2
1 2
2 3
3 4
4 5
5 6
6 7

"""

Submission status: Accepted

Solution code:
#####################################################################################################
##### Low Link (橋、関節点)
#####################################################################################################

"""

計算量
O(V+E)

参考
https://algo-logic.info/bridge-lowlink/


"""



import sys
input = sys.stdin.readline
import marshal


class Graph:
    def __init__(self, n, dictated=False, decrement=True, destroy=False, edges=[]):
        self.n = n
        self.dictated = dictated
        self.decrement = decrement
        self.destroy = destroy
        self.edges = [set() for _ in range(self.n)]
        self.parent = [-1]*self.n
        for x, y in edges:
            self.add_edge(x,y)

    def add_edge(self, x, y):
        if self.decrement:
            x -= 1
            y -= 1
        self.edges[x].add(y)
        if self.dictated == False:
            self.edges[y].add(x)

    def add_adjacent_list(self, i, adjacent_list):
        if self.decrement:
            self.edges[i] = set(map(lambda x: x - 1, adjacent_list))
        else:
            self.edges[i] = set(adjacent_list)

    def bridge_detector(self, articular_list=False):
        """
        :param p: スタート地点
        :param save: True = 前回の探索結果を保持する
        :param articular_list: True = 関節点のリストを返す
        :return: 各点までの距離と何番目に発見したかを返す
        """

        if self.destroy:
            edge2 = self.edges
        else:
            edge2 = marshal.loads(marshal.dumps(self.edges))

        start, time = 0, 0
        p, t = start, time
        self.parent[p] = -2
        ord = [-1]*self.n       # ord = DFS で頂点 q を何番目に探索したか
        ord[p] = t
        low = [-1]*self.n       # low = q からの 0 回以上木辺を使って前進し、後退辺を1回以下使って後退する事で到達できる頂点 p について、ord[p] の最小値
        low[p] = 0
        articular = []          # 関節点の集合
        bridge = []             # 橋の集合

        if len(edge2[p]) >= 2:
            """ 根に子が二つ異常存在する場合は、根も関節点 """
            articular.append(p)

        while True:
            if edge2[p]:
                q = edge2[p].pop()
                if q == self.parent[p] and not self.dictated:
                    """ 逆流した時の処理 """
                    """"""""""""""""""""
                    continue
                if self.parent[q] != -1:
                    """ サイクルで同一点を訪れた時の処理 """
                    if ord[q] < low[p]:
                        low[p] = ord[q]
                    """"""""""""""""""""
                    continue
                """ p から q への引継ぎ"""
                """"""""""""""""""""
                ord[q] = low[q] = t + 1
                self.parent[q] = p
                p, t = q, t + 1
            else:
                if p == start and t == time:
                    break
                """ p から進める点がもう無い時の点 p における処理 """
                parent_p = self.parent[p]
                if low[p] < low[parent_p]:
                    low[parent_p] = low[p]
                """ この時点でlow[p]が確定するので、ord[parent[p]] ≤ low[p] を判定しておく"""
                if ord[parent_p] <= low[p]:
                    if ord[parent_p]:
                        articular.append(parent_p + self.decrement)
                    if ord[parent_p] < low[p]:
                        bridge.append((parent_p + self.decrement, p + self.decrement))
                """"""""""""""""""""
                p, t = self.parent[p], t - 1
                """ 点 p から親ノードに戻ってきた時の親ノードにおける処理 """
                """"""""""""""""""""
        if articular_list == False:
            return bridge
        else:
            return articular

    def draw(self):
        """
        :return: グラフを可視化
        """
        import matplotlib.pyplot as plt
        import networkx as nx

        if self.dictated:
            G = nx.DiGraph()
        else:
            G = nx.Graph()
        for x in range(self.n):
            for y in self.edges[x]:
                G.add_edge(x + self.decrement, y + self.decrement)

        nx.draw_networkx(G)
        plt.show()


def inc(A):
    return list(map(lambda x: x+1, A))


def dec(A):
    return list(map(lambda x: x-1, A))

###############################################################################################

N, M = map(int, input().split())
graph = Graph(N, dictated=False, decrement=True, destroy=True)
for _ in range(M):
    x, y = map(int, input().split())
    graph.add_edge(x, y)



print(len(graph.bridge_detector(articular_list=False)))

# graph.draw()

"""
9 10
7 8
8 9
3 6
9 2
1 2
2 3
3 4
4 5
5 6
6 7

"""

Submission status: Accepted

Solution code:
class UF():
  def __init__(self, N):
    self.parents = [-1] * N

  def find(self, n):
    if self.parents[n] < 0:
      return n
    else:
      self.parents[n] = self.find(self.parents[n])
      return self.parents[n]

  def union(self, u,v):
    p1,p2 = self.find(u),self.find(v)
    if p1==p2: return
    if p1 > p2:
      p1,p2 = p2,p1
    self.parents[p1] += self.parents[p2]
    self.parents[p2] = p1

  def same(self, u,v):
    return self.find(u) == self.find(v)

N,M = map(int,input().split())
uf = UF(N+1)
ans = 0
edges = []
for _ in range(M):
  u,v = map(int,input().split())
  edges.append((u,v))

res = 0
for i in range(M):
  uf = UF(N+1)
  for j,edge in enumerate(edges):
    if i==j: continue
    uf.union(edge[0],edge[1])

  u,v = edges[i]
  if not uf.same(u,v):
    res += 1

print(res)

Submission status: Accepted

Solution code:
n, m = map(int, input().split())
l = [[] for i in range(n)]
o = []
ans = 0

for i in range(m):
    a, b = map(int, input().split())
    a -= 1
    b -= 1
    l[a].append(b)
    l[b].append(a)
    o.append([a, b])

def dfs(f):
    if vis[f] == 1:
        return
    vis[f] = 1
    for t in l[f]:
        if f != a or t != b:
            dfs(t)

for a, b in o:
    vis = [0]*n
    dfs(a)
    if vis[b] == 0:
        ans += 1

print(ans)

Submission status: Accepted

Solution code:
def dfs(f):
    if vis[f] == 1:
        return
    vis[f] = 1 #fを訪問済にする
    for t in v[f]: #fとつながっている頂点を読み込む
        if f != a or t != b: #(a,b)の組み合わせでなければ繰り返す
            dfs(t)

n, m = map(int, input().split())
v = [[] for i in range(n)]
e = []

for i in range(m):
    a, b = map(int, input().split())
    a -= 1
    b -= 1
    v[a].append(b)
    v[b].append(a)
    e.append([a, b])

ans = 0

for a, b in e:
    vis = [0]*n
    dfs(a)
    if vis[b] == 0: #aからa-bの辺以外を辿ってbに行けるなら1になる
        ans += 1

print(ans)

Submission status: Accepted

Solution code:
import networkx as nx

n, m = map(int, input().split())
g = nx.Graph()
g.add_nodes_from(range(1, n + 1))
g.add_edges_from([tuple(map(int, input().split())) for i in range(m)])

print(len(tuple(nx.bridges(g))))

Submission status: Accepted

Solution code:
class UnionFind:
    def __init__(self, n):
        self.nodes = n
        self.parents = [i for i in range(n)]
        self.sizes = [1] * n
        self.rank = [0] * n

    def find(self, i): # どの集合に属しているか（根ノードの番号）
        if self.parents[i] == i:
            return i
        else:
            self.parents[i] = self.find(self.parents[i]) # 経路圧縮
            return self.parents[i]

    def unite(self, i, j): # 二つの集合を併合
        pi = self.find(i)
        pj = self.find(j)
        if pi != pj:
            if self.rank[pi] < self.rank[pj]:
                self.sizes[pj] += self.sizes[pi]
                self.parents[pi] = pj
            else:
                self.sizes[pi] += self.sizes[pj]
                self.parents[pj] = pi
                if self.rank[pi] == self.rank[pj]:
                    self.rank[pi] += 1
    def same(self, i, j): # 同じ集合に属するかを判定
        return self.find(i)==self.find(j)

    def get_parents(self): # 根ノードの一覧を取得
        for n in range(self.nodes): # findで経路圧縮する
            self.find(n)
        return self.parents

adj = []
N, M = map(int,input().split())
for m in range(M):
    a,b = map(int,input().split())
    adj.append([a-1,b-1])

ans = 0
for i in range(M): # 取り除く辺番号
    uf = UnionFind(N)
    for j in range(M):
        if (i==j): # 辺を追加しない（取り除く）
            continue
        uf.unite(*adj[j])
    
    if len(set(uf.get_parents()))!=1: # 複数の集合にわかれているか確認
        ans += 1
print (ans)

Submission status: Accepted

Solution code:
n,m = map(int,input().split())
ab = [list(map(int,input().split())) for i in range(m)]
ans = 0
# print(ab)
for i in range(m):
    temp = [[] for i in range(n+1)]
    v = [0]*(n+1)
    for j in range(m):
        if j==i:
            continue
        else:
            temp[ab[j][0]] += [ab[j][1]]
            temp[ab[j][1]] += [ab[j][0]]
    q = [1]
    # print(temp)
    while q:
        a = q.pop()
        for j in temp[a]:
            if v[j] == 0:
                v[j] = 1
                q.append(j)
    if v.count(1) != n:
        ans += 1
    # print(v)
print(ans)

Submission status: Accepted

Solution code:
from collections import deque
n, m = map(int, input().split())
ab = []
for i in range(m):
    a, b = map(int, input().split())
    a-=1; b-=1
    ab.append((a, b))

ans = 0
for i in range(m):
    # グラフ作成
    g = [[] for i in range(n)]
    for j in range(m):
        if i == j: continue
        a, b = ab[j]
        g[a].append(b)
        g[b].append(a)
    # 距離計算
    dist = [-1] * n
    dq = deque([0])
    dist[0] = 0
    while dq:
        now = dq.popleft()
        for to in g[now]:
            if dist[to]>=0: continue
            dist[to] = dist[now] + 1
            dq.append(to)
    if dist.count(-1) > 0:
        ans+=1
print(ans)