CodeEff/ECCO huggingface dataset:
# Original solution
import sys
import copy

readline = sys.stdin.readline

def dfs(p, x):
    global graphtmp, check
    to = graphtmp[x]
    check[x] = 1
    for t in to:
        if p == t or check[t] == 1:
            continue
        dfs(x, t)

def main():
    global graphtmp, check
    N, M = list(map(int, readline().split()))
    graph = [[] for _ in range(N)]
    nodelist = []
    for _ in range(M):
        a, b = list(map(int, readline().split()))
        a -= 1; b-= 1
        nodelist.append([a, b])
        graph[a].append(b)
        graph[b].append(a)
    
    ans = 0
    for nodetmp in nodelist:
        check = [0]*N
        graphtmp = copy.deepcopy(graph)
        graphtmp[nodetmp[0]].remove(nodetmp[1])
        graphtmp[nodetmp[1]].remove(nodetmp[0])
        dfs(-1, 0)
        if 0 in check:
            ans += 1
    print(ans)


if __name__ == "__main__":
    main()


# Optimized solution
import sys

readline = sys.stdin.readline

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
        self.rank = [0] * 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.rank[x] < self.rank[y]:
            self.parents[x] = y
        else:
            self.parents[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1

def main():
    N, M = list(map(int, readline().split()))
    nodelist = []
    for _ in range(M):
        a, b = list(map(int, readline().split()))
        a -= 1; b -= 1
        nodelist.append([a, b])

    ans = 0
    for i in range(M):
        uf = UnionFind(N)
        for j, node in enumerate(nodelist):
            if j == i:
                continue
            uf.union(node[0], node[1])
        parent = []
        for k in range(N):
            parent.append(uf.find(k))
        if len(set(parent)) > 1:
            ans += 1
    print(ans)


if __name__ == "__main__":
    main()
# Original solution
def warshall_floyd(d, n):
    # d: 隣接行列(正方行列)
    # n: 頂点数

    # i -> j の最短距離を計算する
    for k in range(n):
        for i in range(n):
            for j in range(n):
                # i -> k -> j と頂点kを挟む
                d[i][j] = min(d[i][j], d[i][k] + d[k][j])

    return d


# -------------------------------------------------------------------#
import copy


def main():
    # n: 頂点数
    # w: 辺の数
    n, w = list(map(int, input().split()))
    # v -> v ; cost: 0
    d = [[float("Inf")] * n for i in range(n)]
    for i in range(n):
        d[i][i] = 0

    # d[u][v] : 辺u-vのコスト(if not: inf)
    e = []
    for i in range(w):
        # edge: u -> v ; cost : z
        u, v = [int(x) - 1 for x in input().split()]
        e.append([u, v])
        # 無向グラフを想定
        d[u][v] = 1
        d[v][u] = 1

    ans = 0
    for (u, v) in e:
        dd = copy.deepcopy(d)
        dd[u][v] = float("Inf")
        dd[v][u] = float("Inf")

        wd = warshall_floyd(dd, n)

        for res in wd:
            if float("Inf") in res:
                ans += 1
                break

    print(ans)


if __name__ == "__main__":
    main()


# Optimized solution
import copy


def main():
    # n: 頂点数
    # w: 辺の数
    n, w = list(map(int, input().split()))
    # v -> v ; cost: 0
    d = [[False] * n for i in range(n)]

    # d[u][v] : 辺u-vのコスト(if not: inf)
    e = []
    for i in range(w):
        # edge: u -> v ; cost : z
        u, v = [int(x) - 1 for x in input().split()]
        e.append([u, v])
        # 無向グラフを想定
        d[u][v] = True
        d[v][u] = True

    def dfs(graph, visited, x):
        if visited[x]:
            return
        visited[x] = True
        for i in range(n):
            if graph[x][i]:
                dfs(graph, visited, i)

    ans = 0
    for (u, v) in e:
        graph = copy.deepcopy(d)
        graph[u][v] = False
        graph[v][u] = False
        for i in range(n):
            visited = [False for _ in range(n)]
            dfs(graph, visited, i)
            if not all(visited):
                ans += 1
                break
    print(ans)


if __name__ == "__main__":
    main()
# Original solution
import copy


def main():
    # n: 頂点数
    # w: 辺の数
    n, w = list(map(int, input().split()))
    # v -> v ; cost: 0
    d = [[False] * n for i in range(n)]

    # d[u][v] : 辺u-vのコスト(if not: inf)
    e = []
    for i in range(w):
        # edge: u -> v ; cost : z
        u, v = [int(x) - 1 for x in input().split()]
        e.append([u, v])
        # 無向グラフを想定
        d[u][v] = True
        d[v][u] = True

    def dfs(graph, visited, x):
        if visited[x]:
            return
        visited[x] = True
        for i in range(n):
            if graph[x][i]:
                dfs(graph, visited, i)

    ans = 0
    for (u, v) in e:
        graph = copy.deepcopy(d)
        graph[u][v] = False
        graph[v][u] = False
        for i in range(n):
            visited = [False for _ in range(n)]
            dfs(graph, visited, i)
            if not all(visited):
                ans += 1
                break
    print(ans)


if __name__ == "__main__":
    main()


# Optimized solution
def main():
    n, m, *ab = list(map(int, open(0).read().split()))
    e = [[] for _ in range(n)]
    for u, v in zip(*[iter(ab)] * 2):
        e[u - 1].append(v - 1)
        e[v - 1].append(u - 1)

    ans = 0
    for _ in range(m):
        count = 0
        for i, x in enumerate(e):
            if len(x) == 1:
                e[i] = []
                e[x[0]].remove(i)
                count += 1
        if count:
            ans += count
        else:
            break

    print(ans)


if __name__ == "__main__":
    main()
# Original solution
class UnionFind():
  def __init__(self, n):
    self.n = n
    self.parent = [i for i in range(self.n)]
    self.rank = [0] * self.n
  
  def find_root(self, x):
    if self.parent[x] == x:
      return x
    else:
      self.parent[x] = self.find_root(self.parent[x])
      return self.parent[x]
  
  def unite(self, x, y):
    rx = self.find_root(x)
    ry = self.find_root(y)
    if rx == ry:
      return
    if self.rank[rx] < self.rank[ry]:
      self.parent[rx] = ry
    else:
      self.parent[ry] = rx
      if self.rank[rx] == self.rank[ry]:
        self.rank[rx] += 1
  
  def is_same(self, x, y):
    return self.find_root(x) == self.find_root(y)

N, M = list(map(int, input().split()))
edge = []
bridge = 0
for _ in range(M):
  e = list([int(x)-1 for x in input().split()])
  edge.append(e)

for i in range(M):
  uf = UnionFind(N)
  for j in range(M):
    if j == i:
      continue
    uf.unite(edge[j][0], edge[j][1])
  
  root = uf.find_root(0)
  for i in range(1, N):
    if root != uf.find_root(i):
      bridge += 1
      break

print(bridge)

# Optimized solution
class UnionFind():
  def __init__(self, n):
    self.n = n
    self.parent = [i for i in range(self.n)]
    self.rank = [0] * self.n
  
  def find_root(self, x):
    if self.parent[x] == x:
      return x
    else:
      self.parent[x] = self.find_root(self.parent[x])
      return self.parent[x]
  
  def unite(self, x, y):
    rx = self.find_root(x)
    ry = self.find_root(y)
    if rx == ry:
      return
    if self.rank[rx] < self.rank[ry]:
      self.parent[rx] = ry
    else:
      self.parent[ry] = rx
      if self.rank[rx] == self.rank[ry]:
        self.rank[rx] += 1
  
  def is_same(self, x, y):
    return self.find_root(x) == self.find_root(y)

N, M = list(map(int, input().split()))
edge = []
bridge = 0
for _ in range(M):
  e = list([int(x)-1 for x in input().split()])
  edge.append(e)

for i in range(M):
  uf = UnionFind(N)
  for j in range(M):
    if j == i:
      continue
    uf.unite(edge[j][0], edge[j][1])
  if uf.find_root(edge[i][0]) != uf.find_root(edge[i][1]):
    bridge += 1

print(bridge)
# Original solution
import copy

n, m = list(map(int, input().split()))
array = [[int(x) for x in input().split()] for y in range(m)]
t = copy.copy(array)


def solver(start, array, visited, cnt):
    if visited[start]:
        return cnt
    else:
        visited[start] = True

    for i in array[start]:
        if len(set(visited)) == 1:
            return 1
        else:
            cnt = solver(i, array, visited, cnt)

    return cnt


res2 = 0
for i in range(m):
    tmp = array[:i] + array[i + 1:]
    modify = [[] for x in range(n + 1)]
    for value1, value2 in tmp:
        modify[value1].append(value2)
        modify[value2].append(value1)

    visited = [False if x != 0 and x != 1 else True for x in range(n + 1)]
    if not modify[1]:
        continue

    res = 0
    for i in modify[1]:
        res += solver(i, modify, visited, 0)
    else:
        res2 += 1 if res > 0 else 0

print((m - res2))


# Optimized solution
n, m = list(map(int, input().split()))
ab_array = [[int(x) for x in input().split()] for y in range(m)]
graph = [[] for x in range(n + 1)]


def dfs(graph, now, a, b):
    visited[now] = True
    for next in graph[now]:
        if (now == a and next == b) or (now == b and next == a):
            continue
        if not visited[next]:
            dfs(graph, next, a, b)
    return visited.count(False) == 1


for a, b in ab_array:
    graph[a].append(b)
    graph[b].append(a)
ans = 0
for a, b in ab_array:
    start = a
    end = b
    visited = [False for x in range(n + 1)]
    if dfs(graph, 1, a, b):
        ans += 1
print((m - ans))
# Original solution
def dfs(now):
    if vis[now]==1:
        return 0
    vis[now]=1
    for i in range(n):
        if graph[now][i]==1:
            dfs(i)
n,m=list(map(int,input().split()))
edge=[[int(i) for i in input().split()]for i in range(m)]
graph=[[0 for i in range(n)]for i in range(n)]
vis=[0]*n
for i in range(m):
    graph[edge[i][0]-1][edge[i][1]-1]=1
    graph[edge[i][1]-1][edge[i][0]-1]=1
ans=0
for i in range(m):
    graph[edge[i][0]-1][edge[i][1]-1]=0
    graph[edge[i][1]-1][edge[i][0]-1]=0
    vis=[0]*n
    dfs(0)
    connect=False
    for j in range(n):
        if vis[j]==0:
            connect=True
    if connect:
        ans+=1
    graph[edge[i][0]-1][edge[i][1]-1]=1
    graph[edge[i][1]-1][edge[i][0]-1]=1
print(ans)



# Optimized solution
def dfs(now):
    if vis[now]==1:#既に訪問済みであればreturn
        return 0
    vis[now]=1#訪問した記録をつける
    for i in range(n):
        if graph[now][i]==1:#接続していれば、移動
            dfs(i)
n,m=list(map(int,input().split()))
edge=[[int(i) for i in input().split()]for i in range(m)]#入力値を受け取る
graph=[[0 for i in range(n)]for i in range(n)]
vis=[0]*n#既にある頂点を訪問したか管理する配列
for i in range(m):
    graph[edge[i][0]-1][edge[i][1]-1]=1#グラフを生成
    graph[edge[i][1]-1][edge[i][0]-1]=1
ans=0
for i in range(m):
    graph[edge[i][0]-1][edge[i][1]-1]=0#ある辺を消去
    graph[edge[i][1]-1][edge[i][0]-1]=0
    vis=[0]*n
    dfs(0)
    connect=False#全ての点が繋がっているか判断する値
    for j in range(n):
        if vis[j]==0:#訪問していない点があればcout
            connect=True
    if connect:
        ans+=1
    graph[edge[i][0]-1][edge[i][1]-1]=1#消した辺をもとに戻す
    graph[edge[i][1]-1][edge[i][0]-1]=1
print(ans)
# Original solution
def dfs(now):
    if vis[now]==1:#既に訪問済みであればreturn
        return 0
    vis[now]=1#訪問した記録をつける
    for i in range(n):
        if graph[now][i]==1:#接続していれば、移動
            dfs(i)
n,m=list(map(int,input().split()))
edge=[[int(i) for i in input().split()]for i in range(m)]#入力値を受け取る
graph=[[0 for i in range(n)]for i in range(n)]
vis=[0]*n#既にある頂点を訪問したか管理する配列
for i in range(m):
    graph[edge[i][0]-1][edge[i][1]-1]=1#グラフを生成
    graph[edge[i][1]-1][edge[i][0]-1]=1
ans=0
for i in range(m):
    graph[edge[i][0]-1][edge[i][1]-1]=0#ある辺を消去
    graph[edge[i][1]-1][edge[i][0]-1]=0
    vis=[0]*n
    dfs(0)
    connect=False#全ての点が繋がっているか判断する値
    for j in range(n):
        if vis[j]==0:#訪問していない点があればcout
            connect=True
    if connect:
        ans+=1
    graph[edge[i][0]-1][edge[i][1]-1]=1#消した辺をもとに戻す
    graph[edge[i][1]-1][edge[i][0]-1]=1
print(ans)


# Optimized solution
def dfs(now):
    if vis[now]:
        return 0
    vis[now]=1
    for i in range(n):
        if graph[now][i]:
            dfs(i)
n,m=list(map(int,input().split()))
edge=[[int(i) for i in input().split()]for i in range(m)]
graph=[[0 for i in range(n)]for i in range(n)]
for i in range(m):
    graph[edge[i][0]-1][edge[i][1]-1]=1
    graph[edge[i][1]-1][edge[i][0]-1]=1
vis=[0]*n
ans=0
for i in range(m):
    graph[edge[i][0]-1][edge[i][1]-1]=0
    graph[edge[i][1]-1][edge[i][0]-1]=0
    vis=[0]*n
    dfs(0)
    connect=False
    for j in range(n):
        if vis[j]==0:
            connect=True
    if connect:
        ans+=1
    graph[edge[i][0]-1][edge[i][1]-1]=1
    graph[edge[i][1]-1][edge[i][0]-1]=1
print(ans)
# Original solution
n, m = list(map(int, input().split()))
L = [[0, 0] for _ in range(m)]
g = [[] for _ in range(n)]
for j in range(m):
    a, b = list(map(int, input().split()))
    a, b = a-1, b-1
    L[j][0] = a
    L[j][1] = b

from collections import deque

ans = 0
for i in range(m):
    g = [[] for _ in range(n)]
    for j in range(m):
        if j == i:
            continue
        a = L[j][0]
        b = L[j][1]
        g[a].append(b)
        g[b].append(a)
    #print(g)
    s = deque()
    s.append(0)
    visit = [-1]*n
    visit[0] = 0
    while s:
        x = s.pop()
        for new_x in g[x]:
            if visit[new_x] == -1:
                visit[new_x] = visit[x] + 1
                s.append(new_x)
    if min(visit) == -1:
        ans += 1
print(ans)

# Optimized solution
n, m = list(map(int, input().split()))
g = [[] for _ in range(n)]
for i in range(m):
    a, b = list(map(int, input().split()))
    a, b = a-1, b-1
    g[a].append(b)
    g[b].append(a)

def lowlink(g, root=0):
    n = len(g)
    order = [n]*n
    low = [n]*n

    s = [root]
    cnt = 1
    par = [-1]*n
    seq = []
    while s:
        v = s.pop()
        if order[v] != n:
            continue
        order[v] = cnt
        seq.append(v)
        low[v] = cnt
        cnt += 1
        for u in g[v]:
            if order[u] < cnt:
                if par[v] != u:
                    low[v] = min(low[v], order[u])
                    continue
            else:
                par[u] = v
                s.append(u)
    child = [[] for _ in range(n)]
    for v in range(n):
        if par[v] != -1:
            child[par[v]].append(v)
    seq.reverse()
    for v in seq:
        for u in child[v]:
            low[v] = min(low[v], low[u])
    # bridge
    bridge = []
    for p in range(n):
        for c in child[p]:
            if order[p] < low[c]:
                bridge.append((p, c))

    # articulation points
    AP = []
    for v in range(n):
        if v == root:
            if len(child[v]) >= 2:
                AP.append(v)
        else:
            for c in child[v]:
                if order[v] <= low[c]:
                    AP.append(v)
                    break
    return AP, bridge

_, bridge = lowlink(g, 0)
print((len(bridge)))
# Original solution
from collections import deque
n,m = list(map(int,input().split()))
edge_ls = [0] * m
for i in range(m):
    a,b = list(map(int,input().split()))
    edge_ls[i] = [a,b]
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とyは-=1して使うのが基本
        '''
        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())
edge_q = deque(edge_ls)
num_bridge = 0
for i in range(m):
    tar = edge_q.popleft()
    uf = UnionFind(n)
    for a,b in edge_q:
        a -= 1
        b -= 1
        uf.union(a,b)
    if not uf.group_count() == 1:
        num_bridge += 1
    edge_q.append(tar)
print(num_bridge)

# Optimized solution
from collections import deque
num_ver, num_edge = list(map(int,input().split()))
edges_ls = [0] * num_edge
for i in range(num_edge):
    a,b = list(map(int,input().split()))
    a -= 1
    b -= 1
    edges_ls[i] = [a,b]

num_bridge = 0
for ind_take_edge in range(num_edge):
    graph_ls = [[] for _ in range(num_ver)]
    # graph_lsを作る
    for ind_edge in range(num_edge):
        if ind_edge != ind_take_edge:
            a,b = edges_ls[ind_edge]
            graph_ls[a].append(b)
            graph_ls[b].append(a)
    
    # 0からdfsしてdone_lsを仕上げていく
    done_ls = [0] * num_ver
    queue = deque()
    queue.append(0)
    while queue:
        now = queue.pop()
        done_ls[now] = 1
        for nex in graph_ls[now]:
            if done_ls[nex] != 1:
                queue.append(nex)
    
    if sum(done_ls) != num_ver:
        num_bridge += 1

print(num_bridge)
# Original solution
from collections import deque
N, M = list(map(int, input().split()))

graph = [set() for _ in range(N + 1)]
edge = []
for m in range(M):
    a, b = list(map(int, input().split()))
    graph[a].add(b)
    graph[b].add(a)
    edge.append([a, b])

ans = 0
for m in range(M):
    Bridge = True
    fr, to = edge[m][0], edge[m][1]
    V = {fr} | set(graph[fr]) - {to}
    que = set(graph[fr]) - {to}
    while que:
        node = que.pop()
        for new_node in graph[node]:
            if new_node == to:
                Bridge = False
                break
            if new_node not in V:
                que.add(new_node)
                V.add(new_node)
        else:
            continue
        break
    if Bridge:
        ans += 1
print(ans)

# Optimized solution
from collections import deque
N, M = list(map(int, input().split()))

graph = [set() for _ in range(N + 1)]
edge = []
##immutableに気をつけて
for m in range(M):
    a, b = list(map(int, input().split()))
    graph[a].add(b)
    graph[b].add(a)
    edge.append([a, b])

ans = 0
for m in range(M):
    Bridge = True
    fr, to = edge[m][0], edge[m][1]
    V = {fr} | set(graph[fr]) - {to}
    que = deque(set(graph[fr]) - {to})
    while que:
        node = que.pop()
        for new_node in graph[node]:
            if new_node == to:
                Bridge = False
                break
            if new_node not in V:
                que.append(new_node)
                V.add(new_node)
        else:
            continue
        break
    if Bridge:
        ans += 1
print(ans)
# Original solution
N,M = list(map(int,input().split()))
AB = []
from collections import deque

for i in range(M):
    AB.append(list(map(int,input().split())))


out = 0
for remove in range(M):
    G = [[] for i in range(N)]
    for i in range(M):
        if i==remove:
            continue
        else:
            A,B=AB[i]
            G[A-1].append(B-1)
            G[B-1].append(A-1)
    s = 0
    visit = [0]*N
    que = deque([s])
    visit[s] = 1
    while que:
        v = que.popleft()
        for w in G[v]:
            if visit[w] == 1:
                continue
            visit[w] =  1
            que.append(w)
    if sum(visit)!=N:
        out+=1
print(out)

# Optimized solution
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 = list(map(int,input().split()))

AB = []
for i in range(M):
    AB.append(list(map(int,input().split())))

out = 0
for broken in range(M):
    Network = UnionFind(N)
    for j in range(M):
        if j!=broken:
            A,B=AB[j][0],AB[j][1]
            Network.union(A-1, B-1)
    if Network.group_count()==2:
        out+=1
print(out)
# Original solution
from collections import deque
n,m=list(map(int,input().split()))
X=[]
M=[[] for _ in range(n)]
for i in range(m):
    a,b=list(map(int,input().split()))
    X.append([a,b])
    M[a-1].append(b-1)
    M[b-1].append(a-1)
ans=0
for i in range(m):
    se={X[i][0]-1,X[i][1]-1}
    q=deque([0])
    visited=[0]*n
    while q:
        s=q.popleft()
        if visited[s]==1:
            continue
        visited[s]=1
        for x in M[s]:
            if {s,x}!=se:
                q.append(x)
    if sum(visited)!=n:
        ans+=1
print(ans)

# Optimized solution
n,m=list(map(int,input().split()))
X=[]
M=[[] for _ in range(n)]
for i in range(m):
    a,b=list(map(int,input().split()))
    X.append([a-1,b-1])
    M[a-1].append(b-1)
    M[b-1].append(a-1)
ans=0

def dfs(x,se):
    visited[x]=1
    for m in M[x]:
        if se!={x,m} and visited[m]==0:
            dfs(m,se)


for i in range(m):
    visited=[0]*n
    dfs(0,set(X[i]))
    if 0 in visited:
        ans+=1
print(ans)
# Original solution
from collections import defaultdict
n, m = list(map(int, input().split()))
G = defaultdict(list)
s = []
for i in range(m):
    a, b = list(map(int, input().split()))
    G[a - 1].append(b - 1)
    G[b - 1].append(a - 1)
    s.append((a - 1, b - 1))


def dfs(cur, pre, NG, k):
    global used
    used[cur] = 1
    for v in G[cur]:
        if v != pre and used[v] != 1 and (v, cur) != NG and (cur, v) != NG:
            dfs(v, cur, NG, k + 1)


num = 0
for i in range(m):
    ng = s[i]
    used = [0] * n
    used[0] = 1
    dfs(0, -1, ng, 0)
    if sum(used) == n:
        num += 1

print((m - num))

# Optimized solution
N, M = list(map(int, input().split()))
G = [[] for i in range(N)]
E = []
for i in range(M):
    a, b = list(map(int, input().split()))
    a, b = a - 1, b - 1
    G[a].append(b)
    G[b].append(a)
    E.append((a, b))


def dfs(v, pre, s, ng):
    if used[v]:
        return
    used[v] = 1
    s.add(v)
    for u in G[v]:
        if u == pre:
            continue
        if (u, v) != ng and (v, u) != ng:
            dfs(u, v, s, ng)
    return


num = 0
for i in E:
    used = [0] * N
    s = set()
    dfs(0, -1, s, i)
    if len(s) == N:
        num += 1

print((M - num))