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.

Solution (Python):
n, m = map(int, input().split())
E = [[*map(lambda x: int(x)-1, input().split())] for _ in range(m)]

V = [[] for _ in range(n)]
for a, b in E:
    V[a].append(b)
    V[b].append(a)

def dfs(a, b, f):
    if vis[f] == 1: return
    vis[f] = 1
    for t in V[f]:
        if f==a and t == b: continue
        dfs(a, b, t)

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

Solution (Python):
from collections import deque
n, m = map(int, input().split())
E = [[*map(lambda x: int(x)-1, input().split())] for _ in range(m)]
V = [[] for _ in range(n)]
for a,b in E:
    V[a].append(b)
    V[b].append(a)

def dfs(a,b,f,i):
    
    
    if vis[f] == 1: return
    vis[f] = 1
    
    for t in V[f]:
        if f==a and t == b: continue
        
        if vis[t] == 1: continue
        
        dfs(a,b,t,i+1)

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




print(ans)

Solution (Python):
N, M = map(int, input().split())
G = [[] for _ in range(N)]

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

def bridge(G, N):
    result = set()
    label = [None] * N
    gen = 0
    cost = [0] * N
    
    def dfs(u, p):
        nonlocal gen
        res = 0
        for v in G[u]:
            if v == p:
                continue
            if label[v] is not None:
                if label[v] < label[u]:
                    cost[v] += 1
                    res += 1
            else:
                label[v] = gen
                gen += 1
                r = dfs(v, u)
                if r == 0:
                    result.add((u, v) if u < v else (v, u))
                res += r
        res -= cost[u]
        return res

    for v in range(N):
        if not label[v]:
            label[v] = gen
            gen += 1
            r = dfs(v, -1)
            assert r == 0, r
    return result

res = bridge(G, N)
print(len(res))

Solution (Python):
from collections import defaultdict

N, M = map(int, input().split())
*l, = [tuple(map(int, input().split())) for _ in range(M)]
ans = M

for i in range(M):
    d = defaultdict(list)
    for j in range(M):
        if j==i:continue
        a, b = l[j]
        d[a].append(b)
        d[b].append(a)
    q = [a]
    used = [False]*(N+1)
    used[0] = True
    used[a] = True
    while q:
        s = q.pop()
        for t in d[s]:
            if used[t]:continue
            used[t] = True
            q.append(t)
    if all(used):
        ans -= 1
print(ans)

Solution (Python):
n, m = map(int, input().split())
a, b = [0] * m, [0] * m
for i in range(m):
    a[i], b[i] = map(int, input().split())





def root(x):
    if par[x] == x:
        return x
    else:
        par[x] = root(par[x])
        return par[x] 

def same(x, y):
    return root(x) == root(y)

def unite(x, y):
    x = root(x)
    y = root(y)
    if x == y:
        return
    par[x] = root(y)

cnt = 0
for i in range(m):
    par = [k for k in range(n+1)]
    for j in range(m):
        if i == j:
            continue
        unite(a[j], b[j])
    if not same(a[i], b[i]):
        cnt += 1
print(cnt)

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



def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x])
        return par[x]

def unite(x,y):
    x = find(x)
    y = find(y)
    
    if x == y:
        return False
    else:
        
        if par[x] > par[y]:
            x,y = y,x
        par[x] += par[y]
        par[y] = x
        return True


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


def size(x):
    return -par[find(x)]


ans=0
for i in range(m):
    
    par = [-1]*n
    for j in range(m):
        if j==i:
            continue
        else:
            unite(side[j][0],side[j][1])
    if not same(side[i][0],side[i][1]):
        ans+=1
print(ans)

Solution (Python):
from collections import defaultdict
from copy import deepcopy
import sys


def input(): return sys.stdin.readline().strip()
def I(): return int(input())
def LI(): return list(map(int, input().split()))


class UnionFind():
    def __init__(self, n):
        self.n = n
        
        self.parents = [-1] * n

    def find(self, x):
        '''要素 x の親要素の番号を返す'''
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        '''要素 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

    def size(self, x):
        '''要素 x が属する集合の要素数を返す'''
        return -self.parents[self.find(x)]

    def same(self, x, y):
        '''要素 x と要素 y が同じ集合に属するかを返す'''
        return self.find(x) == self.find(y)

    def members(self, x):
        '''要素 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())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


def main():
    n, m = LI()
    edges = [LI() for _ in range(m)]
    ans = 0
    for remove_u, remove_v in edges:
        uf = UnionFind(n)
        for u, v in edges:
            if u == remove_u and v == remove_v:
                continue
            u, v = u - 1, v - 1
            uf.union(u, v)
        if len(uf.roots()) > 1:
            ans += 1
    print(ans)


main()

Solution (Python):
from collections import defaultdict
from copy import deepcopy
import sys


def input(): return sys.stdin.readline().strip()
def I(): return int(input())
def LI(): return list(map(int, input().split()))


def dfs(graph, v, visited):
    visited.add(v)
    for nv in graph[v]:
        if nv not in visited:
            dfs(graph, nv, visited)
    return visited


def is_connected(graph, nodes_cnt):
    visited = dfs(graph, 1, set())
    
    if len(visited) == nodes_cnt:
        return True
    else:
        return False


n, m = LI()
edges = [LI() for _ in range(m)]
g = defaultdict(set)
for u, v in edges:
    g[u].add(v)
    g[v].add(u)

ans = 0

for u, v in edges:
    H = deepcopy(g)
    H[u] -= {v}
    H[v] -= {u}
    if not is_connected(H, n):
        ans += 1
print(ans)

Solution (Python):
from collections import defaultdict
from copy import deepcopy


def dfs(graph, v, visited):
    visited.add(v)
    for nv in graph[v]:
        if nv not in visited:
            dfs(graph, nv, visited)
    return visited


def is_connected(graph, nodes_cnt):
    visited = dfs(graph, 1, set())
    if len(visited) == nodes_cnt:
        return True
    else:
        return False


N, M = map(int, input().split())
edges = [list(map(int, input().split())) for _ in range(M)]
G = defaultdict(set)
for u, v in edges:
    G[u].add(v)
    G[v].add(u)
ans = 0
for u, v in edges:
    H = deepcopy(G)
    H[u] -= {v}
    H[v] -= {u}
    if not is_connected(H, N):
        ans += 1
print(ans)

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


def dfs(now):
    global cnt
    if not 0 in vis: 
        cnt += 1
        return None
    for new in path[now]:
        if vis[new] == 0:
            vis[new] = 1
            dfs(new)
cnt = 0
for i in range(M):
    vis  = [0 for _ in range(N)]
    a = save[i][0]
    b = save[i][1]
    path[a].remove(b)
    path[b].remove(a)
    vis[0] = 1
    dfs(0)
    path[a].append(b)
    path[b].append(a)
print(M-cnt)

Solution (Python):
import sys
input=sys.stdin.readline
sys.setrecursionlimit(10 ** 8)
from itertools import accumulate
from itertools import permutations
from itertools import combinations
from collections import defaultdict
from collections import Counter
import fractions
import math
from collections import deque
from bisect import bisect_left
from bisect import bisect_right
from bisect import insort_left
import itertools
from heapq import heapify
from heapq import heappop
from heapq import heappush
import heapq
from copy import deepcopy
from decimal import Decimal
alf = list("abcdefghijklmnopqrstuvwxyz")
ALF = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")

INF = float("inf")


N,M = map(int,input().split())
fr = [[] for _ in range(N)]

AB = [list(map(int,input().split())) for _ in range(M)]
for i in range(M):
    a,b = AB[i]
    a,b = a-1,b-1
    fr[a].append(b)
    fr[b].append(a)
def dfs(fr,cur,parent,index,li):
    children = fr[cur]
    for chi in children:
        if chi == parent or used[chi] != -1:
            continue
        used[chi] = index
        li.append(chi)
        dfs(fr,chi,cur,index,li)
cnt = 0
for i in range(M):
    a,b = AB[i]
    a,b = a-1,b-1
    used = [-1]*N 
    renketu = []
    index = 0
    fr[a].remove(b)
    fr[b].remove(a)
    for j in range(N):
        if used[j] != -1:
            continue
        li = [j]
        used[j] = index
        dfs(fr,j,-1,index,li)
        index += 1
        renketu.append(li)
    if len(renketu) != 1:
        cnt += 1
    fr[a].append(b)
    fr[b].append(a)
print(cnt)

Solution (Python):
def find(n):
    if root[n] == n:
        return n
    else:
        parent = root[n]
        root[n] = find(parent)
        return root[n]

def unite(x,y):
    rx = find(x)
    ry = find(y)
    if rx == ry:
        return
    else:
        root[rx] = ry

def same(x,y):
    if find(x) == find(y):
        return True
    else:
        return False

def member(x):
    root = find(x)
    return [i for i in range(n) if find(i) == root]

def member_count(x):
    return len(member(x))

n , m = map(int,input().split())
route = [tuple(map(lambda x:int(x)-1,input().split())) for i in range(m)]
ans = 0
for i in range(m):
    root = [k for k in range(n)]
    for j in range(m):
        if i == j:
            continue
        a,b = route[j]
        unite(a,b)
    if member_count(0) != n:
        ans += 1
        
print(ans)

Solution (Python):
def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x])
        return par[x]
def unite(x,y):
    x = find(x)
    y = find(y)
    if x == y: return False
    
    if par[x] > par[y]: x,y = y,x
    par[x] += par[y]
    par[y] = x
    return True

n, m = map(int, input().split())
ab = []
ans = m
for _ in range(m):
    a, b = map(int, input().split())
    ab.append([a-1, b-1])
for i in range(m):
    par = [-1]*n
    for j in range(m):
        if i==j: continue
        unite(ab[j][0], ab[j][1])
    if -min(par) == n: ans -= 1
print(ans)

Solution (Python):


import sys
input=sys.stdin.readline

class UnionFind:
    def __init__(self, n):
        self.p=[-1]*n
        
    def root(self, x):
        st=[]
        while(self.p[x]>=0):
            st.append(x)
            x=self.p[x]
        for y in st:
            self.p[y]=x
        return x
    def find(self, x, y):
        return self.root(x)==self.root(y)
    def union(self, x, y):
        r1=self.root(x)
        r2=self.root(y)
        if r1==r2:
            return
        up=self.p
        p1=up[r1]
        p2=up[r2]
        if p1<=p2:
            up[r2]=r1
            if p1==p2:
                up[r1]-=1
        else:
            up[r1]=r2
            
def main():
    N,M=map(int,input().split())
    AB=[tuple(map(lambda x: int(x)-1,input().split())) for i in range(M)]
    
    ans=0
    for i in range(M):
        uft=UnionFind(N)
        for j in range(M):
            if i!=j:
                uft.union(AB[j][0],AB[j][1])
        ps=0
        for x in range(N):
            if uft.p[x]<0:
                ps+=1
        if ps!=1:
            ans+=1
    print(ans)
        
    
    
    
    
if __name__=="__main__":
    main()

Solution (Python):
n,m=map(int,input().split())
l=list()
for i in range(m):
    l.append(list(map(int,input().split())))
p=0
for o in range(m):
    d={i:[] for i in range(1,n+1)}
    for i in range(m):
        if i==o:
            continue
        a,b=l[i]
        d[a].append(b)
        d[b].append(a)
    q=[1]
    s={i:1 for i in range(1,n+1)}
    while len(q)>0:
        for i in d[q.pop()]:
            if s[i]:
                s[i]=0
                q.append(i)
    
    if 1 in s.values():
        p+=1
print(p)

Solution (Python):

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

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

    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

    def size(self, x):
        return -self.parents[self.find(x)]

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

    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())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

(n, m), *q = [list(map(int, o.split())) for o in open(0)]
c = 0
for i in range(m):
    UF = UnionFind(n)
    for j, (a, b) in enumerate(q):
        if i == j:continue
        UF.union(a - 1, b - 1)
    if UF.group_count() > 1:
        c += 1
print(c)

Solution (Python):
N,M = map(int,input().split())
es = []
for i in range(M):
    a,b = map(int,input().split())
    a,b = a-1, b-1
    es.append((a,b))
 
parent = None
def root(a):
    if parent[a] == a:
        return a
    else:
        parent[a] = root(parent[a])
        return parent[a]
def is_same(a,b):
    return root(a) == root(b)
def unite(a,b):
    ra = root(a)
    rb = root(b)
    if ra == rb: return
    parent[ra] = rb;
 
ans = 0
for i in range(M):
    parent = [k for k in range(N)]
    for j,e in enumerate(es):
        if j == i: continue
        a,b = e
        unite(a,b)
    a,b = es[i]
    if not is_same(a,b):
        ans += 1
print(ans)

Solution (Python):
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): 
            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)

Solution (Python):
N, M = map(int, input().split())
ab = [list(map(int, input().split())) for i in range(M)]

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        '''
        要素xが属するグループの根を返す
        '''
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
    
    def union(self, x, y):
        '''
        要素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

    def size(self, x):
        '''
        要素xが属するグループのサイズ（要素数）を返す
        '''
        return -self.parents[self.find(x)]

    def same(self, x, y):
        '''
        要素x, yが同じグループに属するかどうかを返す
        '''
        return self.find(x) == self.find(y)

    def members(self, x):
        '''
        要素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())

    def all_group_members(self):
        '''
        {ルート要素: [そのグループに含まれる要素のリスト], ...}の辞書を返す
        '''
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

ans = 0
for i in range(M):
    G = UnionFind(N)
    for j in range(M):
        if i == j:
            continue
        a, b = ab[j]
        a, b = a-1, b-1
        G.union(a, b)
    if G.group_count() > 1:
        ans += 1
print(ans)

Solution (Python):
from collections import deque

N, M = map(int, input().split())

edges = [list(map(int, input().split())) for _ in range(M)]
od = [-1]*(N+1)
low = [-1]*(N+1)
pa = [0]*(N+1)
od[1] = 0
low[1] = 0
pa[1] = 1
ni = {i:[] for i in range(1,N+1)}

for i in range(M):
  ni[edges[i][0]].append(edges[i][1])
  ni[edges[i][1]].append(edges[i][0])

rlt = 0
h = deque([])
for i in ni[1]:
  h.append([1,i])

i = 0
while h:
  a = h.pop()
  if od[a[1]] > -1:
    if od[a[1]] < od[a[0]]:
      low[a[0]] = od[a[1]]
      c = a[0]
      while low[pa[c]] > od[a[1]]:
        low[pa[c]] = od[a[1]]
        c = pa[c]
  else:
    i += 1
    od[a[1]] = i
    low[a[1]] = i
    pa[a[1]] = a[0]
    for b in ni[a[1]]:
      if b == a[0]:
        continue
      h.append([a[1],b])
          
for i in range(2,N+1):
  if od[pa[i]] < low[i]:
    rlt += 1

print(rlt)

Solution (Python):
class UnionFind():
  def __init__(self, n):
    self.n = n
    self.parents = [-1] * n

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

  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

  def size(self, x):
    return -self.parents[self.find(x)]

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

  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())

  def all_group_members(self):
    return {r: self.members(r) for r in self.roots()}

  def __str__(self):
    return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

N, M = map(int, input().split())

edges = [list(map(int, input().split())) for _ in range(M)]

rlt = 0

for i in range(M):
  uf = UnionFind(N+1)
  for j in range(M):
    if i == j:
      continue
    e = edges[j]
    uf.union(e[0],e[1])
  if min(uf.parents) > -N:
    rlt += 1
    
print(rlt)

Solution (Python):
class UnionFind:
    def __init__(self, n):
        self.n = n
        self.parent = [i for i in range(n)]  
        self.rank = [1] * n  
        self.size = [1] * n  
 
    def find(self, x):  
        if self.parent[x] == x:
            return x
        else:
            self.parent[x] = self.find(self.parent[x])  
            return self.parent[x]
 
    def unite(self, x, y):  
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                self.parent[x] = y
                self.size[y] += self.size[x]
            else:
                self.parent[y] = x
                self.size[x] += self.size[y]
                if self.rank[x] == self.rank[y]:
                    self.rank[x] += 1
 
    def is_same(self, x, y):  
        return self.find(x) == self.find(y)
 
    def group_size(self, x):  
        return self.size[self.find(x)]




def bridge(edges,v,e):
    hashi=[]
    for i in range(e):
        uf=UnionFind(v)
        for j in range(e):
            if i==j:
                continue
            a,b=edges[j]
            uf.unite(a,b)
        if uf.group_size(0)!=v:
            hashi.append(i)
    return hashi

def is_bridge(edges,v,e,i):
    uf=UnionFind(v)
    for j in range(e):
        if i==j:
            continue
        a,b=edges[j]
        uf.unite(a,b)
    if uf.group_size(0)!=v:
        return True
    else:
        return False

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

ans=0
for i in range(m):
    if is_bridge(edges,n,m,i):
        ans+=1
print(ans)

Solution (Python):
class UnionFind:
    def __init__(self, n):
        self.n = n
        self.parent = [i for i in range(n)]  
        self.rank = [1] * n  
        self.size = [1] * n  
 
    def find(self, x):  
        if self.parent[x] == x:
            return x
        else:
            self.parent[x] = self.find(self.parent[x])  
            return self.parent[x]
 
    def unite(self, x, y):  
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                self.parent[x] = y
                self.size[y] += self.size[x]
            else:
                self.parent[y] = x
                self.size[x] += self.size[y]
                if self.rank[x] == self.rank[y]:
                    self.rank[x] += 1
 
    def is_same(self, x, y):  
        return self.find(x) == self.find(y)
 
    def group_size(self, x):  
        return self.size[self.find(x)]




def bridge(edges,v,e):
    hashi=[]
    for i in range(e):
        uf=UnionFind(v)
        for j in range(e):
            if i==j:
                continue
            a,b=edges[j]
            uf.unite(a,b)
        if uf.group_size(0)!=v:
            hashi.append(edges[i])
    return hashi

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

print(len(bridge(edges,n,m)))

Solution (Python):
def c_bridge_networkx():
    import networkx as nx
    N, M = [int(i) for i in input().split()]
    Edges = [tuple([int(i) for i in input().split()]) for j in range(M)]

    graph = nx.Graph()
    graph.add_nodes_from(range(1, N + 1))
    graph.add_edges_from(Edges)
    
    return len(tuple(nx.bridges(graph)))

print(c_bridge_networkx())

Solution (Python):
import sys
import copy
sys.setrecursionlimit(10000)

def root(x):
    if par[x] == x:
        return x
    else:
        par[x] = root(par[x])
        return par[x]

def unite(x, y):
    rx = root(x)
    ry = root(y)
    if rx != ry:
        par[rx] = ry

def same(x, y):
    return root(x) == root(y)


n, m = map(int, input().split())

par = [i for i in range(n)]

a = [0] * m
b = [0] * m

for i in range(m):
    a[i], b[i] = map(int, input().split())

ans = 0

for i in range(m):
    c = copy.deepcopy(a)
    d = copy.deepcopy(b)
    c = c[:i] + c[i+1:]
    d = d[:i] + d[i+1:]
    par = [i for i in range(n)]
    for j in range(m-1):
        unite(c[j]-1, d[j]-1)
    if not same(a[i]-1, b[i]-1):
        ans += 1

print(ans)

Solution (Python):
import sys
import itertools

import time
import math
 
sys.setrecursionlimit(10 ** 7)
 
from collections import defaultdict
 
read = sys.stdin.buffer.read
readline = sys.stdin.buffer.readline
readlines = sys.stdin.buffer.readlines

N, M = map(int, input().split())


class UnionFind(object):
    def __init__(self, n = 1):
        self.link = [i for i in range(n)]
        self.size = [1 for _ in range(n)]

    def find(self, x):
        if self.link[x] == x:
            return x

        
        
        self.link[x] = self.find(self.link[x])
        return self.link[x]

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

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

        if self.size[x] < self.size[y]:
            x, y = y, x
        self.link[y] = x
        self.size[x] += self.size[y]
    
    def get_size(self, x):
        x = self.find(x)
        return self.size[x]

es = [0] * M
for i in range(M):
    a, b = map(int, input().split())
    es[i] = (a - 1, b - 1)

ans = 0
for i in range(M):
    u = UnionFind(N)
    for j in range(M):
        if i == j:
            continue
        a, b = es[j]
        u.unite(a, b)
    if u.get_size(0) != N:
        ans += 1
print(ans)

Solution (Python):


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

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

    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                                     

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

    def size(self, x):                                          
        return -self.parents[self.find(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())

    def all_group_members(self):                                
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):                                          
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


N, M = map(int, input().split())

data = [tuple(map(int, input().split())) for _ in range(M)]
res = 0

for k in range(M):
    tree = UnionFind(N)
    for a, b in data[:k] + data[k+1:]:
        a -= 1
        b -= 1
        tree.union(a,b)
    if tree.group_count() > 1:
       res += 1
print(res)

Solution (Python):
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 _ in range(M)])

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

Solution (Python):
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

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

    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

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

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())
        
n, m = map(int, input().split())
edge = []
for i in range(m):
    a, b = map(int, input().split())
    edge.append((a - 1, b - 1))

ans = 0
for i in range(m):
    uf = UnionFind(n)
    for j in range(m):
        if j == i:
            continue
        uf.union(edge[j][0], edge[j][1])
    if uf.group_count() != 1:
        ans += 1

print(ans)

Solution (Python):
def find(x):
  if par[x] < 0:
    return x
  else:
    par[x] = find(par[x])
    return par[x]

def unite(x,y):
  x,y=find(x),find(y)
  if x!=y:
    if x>y:
        x,y=y,x
    par[x]+=par[y] 
    par[y]=x 

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

def size(x):
  return-par[find(x)]

N, M = map(int, input().split())
par = [-1] * N
def shokika():
  for i in range(N):
    par[i] = -1
    
def main():
  edge = []
  for i in range(M):
    a, b = map(int, input().split())
    a -= 1
    b -= 1
    edge.append([a, b])
  ans = 0
  for i in range(M):
    shokika()
    for j in range(M):
      if i == j:continue
      else:
        unite(edge[j][0], edge[j][1])
    top =  find(0)
    for j in range(1, N):
      if top != find(j):
        ans += 1
        break
  print(ans)
  
if __name__ == "__main__":
  main()