CodeEff/ECCO huggingface dataset:
# Original solution
import sys
sys.setrecursionlimit(10**8)
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for _ in range(N)]
def dp2(ini, i, j): return [[ini]*i for _ in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for _ in range(j)] for _ in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value
#from itertools import accumulate #list(accumulate(A))
import copy
N, M = mi()
D = dp2(float('inf'), N, N)
memo = []
cnt = 0

for i in range(M):
    s, t = mi()
    s, t = s-1, t-1
    memo.append((s, t))
    D[s][t] = D[t][s] = 1

for x in range(M):
    d = copy.deepcopy(D)
    s, t = memo[x][0], memo[x][1]
    d[s][t] = d[t][s] = float('inf')
    for k in range(N):
        for i in range(N):
            for j in range(N):
                d[i][j] = min(d[i][j], d[i][k] + d[k][j])

    for i in range(N):
        for j in range(i+1, N):
            if d[i][j] == float('inf'):
                cnt += 1
                break
        else:
            continue
        break

print(cnt)

# Optimized solution
import sys
sys.setrecursionlimit(10**8)
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for _ in range(N)]
def dp2(ini, i, j): return [[ini]*i for _ in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for _ in range(j)] for _ in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value
#from itertools import accumulate #list(accumulate(A))

## UnionFind

N, M = mi()
memo = []
rel = [[] for i in range(N)]
#d = dp2(float('inf'), N, N)

for i in range(M):
    s, t = mi()
    s, t = s-1, t-1
    memo.append((s, t))
    rel[s].append(t)
    rel[t].append(s)
    #d[s][t] = d[t][s] = 1
#print(d)

def dfs(v):
    #print(v, used)
    for nv in rel[v]:
    #for nv in range(N):
        if not used[nv]:
        #if d[v][nv] != float('inf') and not used[nv]:
            used[nv] = 1
            dfs(nv)
            if sum(used) == N:
                global flag
                flag = True
                return
            #used[nv] = 0

cnt = M
for x in range(M):
    s, t = memo[x][0], memo[x][1]
    rel[s].remove(t)
    rel[t].remove(s)
    #print(rel)
    #print(s, t)
    #d[s][t] = d[t][s] = float('inf')
    #print(d)
    for i in range(N):
        #print(x, i)
        flag = 0
        used = [0]*N
        used[i] = 1
        dfs(i)
        #print(flag)
        if flag:
            cnt -= 1
            break
    rel[s].append(t)
    rel[t].append(s)
    #d[s][t] = d[t][s] = 1

print(cnt)
# Original solution
import sys
sys.setrecursionlimit(10**8)
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for _ in range(N)]
def dp2(ini, i, j): return [[ini]*i for _ in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for _ in range(j)] for _ in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value
#from itertools import accumulate #list(accumulate(A))

## UnionFind

N, M = mi()
memo = []
rel = [[] for i in range(N)]
#d = dp2(float('inf'), N, N)

for i in range(M):
    s, t = mi()
    s, t = s-1, t-1
    memo.append((s, t))
    rel[s].append(t)
    rel[t].append(s)
    #d[s][t] = d[t][s] = 1
#print(d)

def dfs(v):
    #print(v, used)
    for nv in rel[v]:
    #for nv in range(N):
        if not used[nv]:
        #if d[v][nv] != float('inf') and not used[nv]:
            used[nv] = 1
            dfs(nv)
            if sum(used) == N:
                global flag
                flag = True
                return
            #used[nv] = 0

cnt = M
for x in range(M):
    s, t = memo[x][0], memo[x][1]
    rel[s].remove(t)
    rel[t].remove(s)
    #print(rel)
    #print(s, t)
    #d[s][t] = d[t][s] = float('inf')
    #print(d)
    for i in range(N):
        #print(x, i)
        flag = 0
        used = [0]*N
        used[i] = 1
        dfs(i)
        #print(flag)
        if flag:
            cnt -= 1
            break
    rel[s].append(t)
    rel[t].append(s)
    #d[s][t] = d[t][s] = 1

print(cnt)

# Optimized solution
import sys
sys.setrecursionlimit(10**8)
def ii(): return int(sys.stdin.readline())
def mi(): return list(map(int, sys.stdin.readline().split()))
def li(): return list(map(int, sys.stdin.readline().split()))
def li2(N): return [list(map(int, sys.stdin.readline().split())) for _ in range(N)]
def dp2(ini, i, j): return [[ini]*i for _ in range(j)]
def dp3(ini, i, j, k): return [[[ini]*i for _ in range(j)] for _ in range(k)]
#import bisect #bisect.bisect_left(B, a)
#from collections import defaultdict #d = defaultdict(int) d[key] += value
#from itertools import accumulate #list(accumulate(A))

## DFS

N, M = mi()
memo = []
rel = [[] for i in range(N)]

for i in range(M):
    s, t = mi()
    s, t = s-1, t-1
    memo.append((s, t))
    rel[s].append(t)
    rel[t].append(s)

def dfs(v):
    for nv in rel[v]:
        if not used[nv]:
            used[nv] = 1
            dfs(nv)
            if sum(used) == N:
                global flag
                flag = 1
                return

cnt = M
for x in range(M):
    s, t = memo[x][0], memo[x][1]
    rel[s].remove(t)
    rel[t].remove(s)
    flag = 0
    used = [0]*N
    dfs(0)
    if flag:
        cnt -= 1
    rel[s].append(t)
    rel[t].append(s)

print(cnt)
# Original solution
N, M = list(map(int, input().split()))
graph = [[0 for i in range(N)] for j in range(N)]
edges = []
for m in range(M):
    a, b = list(map(int, input().split()))
    a -= 1
    b -= 1
    edges.append([a,b])
    graph[a][b] = 1
    graph[b][a] = 1

ans = 0
for a,b in edges:
    graph[a][b] = 0
    graph[b][a] = 0

    visited = [False] * N
    visited[0] = True
    q = [i for i in range(N) if graph[0][i]]

    while q:
        r = q.pop(0)
        visited[r] = True
        for i in range(N):
            if graph[r][i] and not visited[i]:
                q.append(i)

    if False in visited:
        ans += 1

    graph[a][b] = 1
    graph[b][a] = 1

print(ans)

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

def main():
    N, M = list(map(int, input().split()))
    G = []
    for _ in range(M):
        a, b = list(map(int, input().split()))
        a -= 1
        b -= 1
        G.append((a,b))
    ans = 0
    for i in range(M):
        uni = UnionFind(N)
        for j in range(M):
            if i == j:
                continue
            uni.union(G[j][0],G[j][1])
        if uni.group_count() >= 2:
            ans += 1

    print(ans)
if __name__ == "__main__":
    main()
# Original solution
n, m = list(map(int, input().split()))
d = [[float("inf")] * n for i in range(n)]
edge = []
for i in range(m):
    a, b = list(map(int, input().split()))
    edge.append((a, b))
    d[a - 1][b - 1] = 1
    d[b - 1][a - 1] = 1
ans = 0
for a in range(m):
    d_s = [x[:] for x in d]
    x, y = edge[a]
    d_s[x - 1][y - 1] = float("inf")
    d_s[y - 1][x - 1] = float("inf")
    for k in range(n):
        for i in range(n):
            for j in range(n):
                d_s[i][j] = min(d_s[i][j], d_s[i][k] + d_s[k][j])
    flag = 0
    for i in range(n):
        if flag:
            break
        for j in range(n):
            if flag:
                break
            if d_s[i][j] == float("inf"):
                ans += 1
                flag = 1
print(ans)

# Optimized solution
n, m = list(map(int, input().split()))
edge = [list(map(int, input().split())) for i in range(m)]
ans = 0
for i in edge:
    l = list(range(n))
    for j in edge:
        if j != i:
            l = [l[j[0] - 1] if l[k] == l[j[1] - 1] else l[k] for k in range(n)]
    if len(set(l)) != 1:
        ans += 1
print(ans)
# Original solution
n, m = list(map(int, input().split()))
li = []
count = 0

def check_dfs(node_list):
    traveled = []
    return dfs(node_list, traveled, 0)


def dfs(node_list, t_list, position):
    t_list.append(position)
    if len(t_list) == n:
        return 0
    for i in node_list[position]:
        if i not in t_list:
            dfs(node_list, t_list, i)
    if len(t_list) == n:
        return 0
    else:
        return 1

for i in range(m):
    array = list(map(int, input().split()))
    li.append(array)

for i in range(m):
    nodes = [[] for k in range(n)]
    for j in range(m):
        if j == i:
            continue
        else:
            nodes[li[j][0]-1].append(li[j][1]-1)
            nodes[li[j][1]-1].append(li[j][0]-1)
    count += check_dfs(nodes)

print(count)

# Optimized solution
import sys
sys.setrecursionlimit(10**5)

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

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

cnt = 0


def dfs(start, a, b, explored, n):
    for node in graph[start]:
        if node not in explored:
            if (start, node) == (a, b) or (start, node) == (b, a):
                continue
            else:
                explored.append(node)
                dfs(node, a, b, explored, n)
    if len(explored) == n:
        return 0
    else:
        return 1
x = 0
for i, j in edge:
    x += dfs(0, i, j, [], n)


print(x)
# Original solution
N, M = list(map(int, input().split()))

graph = [[False for j in range(N)] for i in range(N)]

am = [0] * M
bm = [0] * M


def dfs(visited, n):
    visited[n] = True

    for k, v in enumerate(visited):
        if v:
            continue

        if not graph[n][k]:
            continue

        visited = dfs(visited, k)

    return visited


def main():
    for i in range(M):
        # 0-indexed
        ai, bi = list(map(int, input().split()))
        ai -= 1
        bi -= 1

        am[i] = ai
        bm[i] = bi

        graph[ai][bi] = True
        graph[bi][ai] = True

    ans = 0

    for ai, bi in zip(am, bm):
        graph[ai][bi] = False
        graph[bi][ai] = False

        # 訪問済 All False
        visited = dfs([False] * N, 0)

        # All True ならば 橋 ではない
        if not all(visited):
            ans += 1

        # 元に戻す
        graph[ai][bi] = True
        graph[bi][ai] = True

    print(ans)
    return


main()


# Optimized solution
N, M = list(map(int, input().split()))

graph = [[] for i in range(N)]

for i in range(M):
    ai, bi = list(map(int, input().split()))
    ai -= 1
    bi -= 1
    graph[ai].append(bi)
    graph[bi].append(ai)

rank = [N] * N
low = [N] * N
visited = [False] * N


def dfs(k, pos, parent):
    rank[pos] = k
    low[pos] = k

    k += 1

    visited[pos] = True

    for i in graph[pos]:
        if not visited[i]:
            dfs(k, i, pos)
            low[pos] = min(low[pos], low[i])
        elif i != parent:
            low[pos] = min(low[pos], rank[i])


def main():
    dfs(0, 0, -1)

    ans = 0

    for i in range(N):
        for j in graph[i]:
            if rank[j] < low[i]:
                ans += 1

    print(ans)
    return


main()
# Original solution
n, m = list(map(int, input().split()))
INF = 10**9
a = [[INF]*n for i in range(n)]
hen_m = []
for i in range(m):
    x1, x2 = list(map(int, input().split()))
    x1 -= 1
    x2 -= 1
    hen_m.append([x1, x2])
    a[x1][x2] = 1
    a[x2][x1] = 1
for i in range(n):
    a[i][i] = 0

#ワーシャルフロイド
def warshall(n, c):
    for i in range(n):
        for j in range(n):
            for k in range(n):
                c[j][k] = min(c[j][k], c[j][i] + c[i][k])

ans = 0
# Mの辺を消して、わ～シャルフロイド
for i in range(m):
    b = [i[::] for i in a]
    x1 , x2 = hen_m[i]
    b[x1][x2] = INF
    b[x2][x1] = INF
    warshall(n, b)
    flag = 0
    for j in range(n - 1):
        for k in range(j + 1,n):
            if b[j][k] >= INF:
                flag = 1
                break
        if flag == 1:
            break
    #print(i, flag)
    ans += flag
print(ans)


# Optimized solution
n, m = list(map(int, input().split()))
a = [[False]*n for i in range(n)]
hen_m = []
for i in range(m):
    x1, x2 = list(map(int, input().split()))
    x1 -= 1
    x2 -= 1
    hen_m.append([x1, x2])
    a[x1][x2] = True
    a[x2][x1] = True
for i in range(n):
    a[i][i] = True

#ワーシャルフロイド
def warshall(n, c):
    for i in range(n):
        for j in range(n):
            for k in range(n):
                c[j][k] = c[j][k] or (c[j][i] and c[i][k])

ans = 0
# Mの辺を消して、わ～シャルフロイド
for i in range(m):
    b = [i[::] for i in a]
    x1 , x2 = hen_m[i]
    b[x1][x2] = False
    b[x2][x1] = False
    warshall(n, b)
    flag = 0
    for j in range(n - 1):
        for k in range(j + 1,n):
            if not b[j][k]:
                flag = 1
                break
        if flag == 1:
            break
    #print(i, flag)
    ans += flag
print(ans)
# Original solution
import sys
sys.setrecursionlimit(10**9)
n,m=list(map(int,input().split()))
ab=[[0,0]  for i in range(m)]
for i in range(m):
    ab[i][0],ab[i][1]=list(map(int,input().split()))
ans=0
for i in range(m):
    root=[j for j in range(n+1)]
    root[0]=1
    def r(x):
        if root[x]==x:
            return x
        else:
            root[x]=r(root[x])
            return root[x]
    def unite(x,y):
        x=r(x)
        y=r(y)
        if x==y:
            return
        else:
            x,y=min(x,y),max(x,y)

            root[y]=x
    for j in range(m):
        if j!=i:
            a,b=ab[j][0],ab[j][1]
            unite(a,b)

    if not all(r(j)==1 for j in range(1,n+1)):
        ans+=1
print(ans)



# Optimized solution
from _collections import deque
n,m=list(map(int,input().split()))
ab=[[0,0] for i in range(m)]
for i in range(m):
    ab[i][0],ab[i][1]=list(map(int,input().split()))
ans=0
for i in range(m):
    edg=[[] for j in range(n+1)]
    dep=[-1]*(n+1)
    dep[0]=1
    dep[1]=1
    for j in range(m):
        if j!=i:
            a,b=ab[j][0],ab[j][1]
            edg[a].append(b)
            edg[b].append(a)
    data=deque([1])
    while len(data)>0:
        p=data.popleft()
        for j in edg[p]:
            if dep[j]==-1:
                dep[j]=1
                data.append(j)
    if not all(dep[i]==1 for i in range(n+1)):ans+=1
print(ans)
# Original solution
class uf_tree:
    def __init__(self, n):
        self.sizes = [0] * n
        self.par = list(range(n))

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

    def unite(self, x, y):
        x, y = self.find(x), self.find(y)
        if x == y:
            return
        if self.sizes[x] < self.sizes[y]:
            x, y = y, x
        self.par[y] = x
        self.sizes[x] += self.sizes[y]

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


N, M = list(map(int, input().split()))
a, b = [0] * M, [0] * M

for i in range(M):
    a[i], b[i] = list(map(int, input().split()))
    a[i] -= 1; b[i] -= 1
ans = 0
for i in range(M):
    uf = uf_tree(N+1)
    for j in range(M):
        if i == j:
            continue
        uf.unite(a[j], b[j])

    bridge = False
    for i in range(N):
        if not uf.same(0, i):
            bridge = True
    if bridge:
        ans += 1

print(ans)


# Optimized solution
#!/usr/bin/env python3

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

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

    graph[a].append(b)
    graph[b].append(a)

def dfs(u, v):
    is_visited = [ False ]*n
    st = []
    st.append(0)
    while st:
        cur = st.pop()
        is_visited[cur] = True
        for adj in graph[cur]:
            if is_visited[adj]: continue
            if cur == u and adj == v: continue
            if cur == v and adj == u: continue

            st.append(adj)

    return all(is_visited)

ans = 0
for u, v in edges:
    if not dfs(u, v):
        ans += 1

print(ans)
# Original solution
import heapq
N, M = list(map(int, input().split()))
ab = [list([int(x)-1 for x in input().split()]) for _ in range(M)]
ans = 0

def dijkstra(s, graph, n):
  dist = [float('inf')]*n
  dist[s] = 0
  num = [0]*n
  num[s] = 1
  q = [[0, s]]
  heapq.heapify(q)
  while q:
    c, u = heapq.heappop(q)
    if dist[u] < c:
      continue
    for v, weight in graph[u]:
      if dist[v] > dist[u] + weight:
        dist[v] = dist[u] + weight
        num[v] = num[u]
        heapq.heappush(q, [dist[v], v])
      elif dist[v] == dist[u] + weight:
        num[v] += num[u]
  return dist, num

for i in range(M):
  graph = [[] for _ in range(N)]
  for j in range(M):
    if i != j:
      a, b = ab[j]
      graph[a] += [[b, 1]]
      graph[b] += [[a, 1]]

  for j in range(N):
    d, _ = dijkstra(j, graph, N)
    if max(d) == float('inf'):
      ans += 1
      break
print(ans)


# Optimized solution
import sys
sys.setrecursionlimit(1000000000)
N, M = list(map(int, input().split()))
graph = [[0]*N for _ in range(N)]
visited = [0]*N
ab = []
ans = 0

def dfs(u):
  visited[u] = 1
  for v in range(N):
    if graph[u][v] == 1 and visited[v] == 0:
      dfs(v)

for _ in range(M):
  a, b = [int(x)-1 for x in input().split()]
  graph[a][b] = 1
  graph[b][a] = 1
  ab += [[a, b]]

for _ in range(M):
  a, b = ab.pop(0)
  graph[a][b] = 0
  graph[b][a] = 0

  for i in range(N):
    visited[i] = 0
  
  dfs(0)
  
  if sum(visited) != N:
    ans += 1

  graph[a][b] = 1
  graph[b][a] = 1

print(ans)
# Original solution
import copy
N, M = [int(_) for _ in input().split()]
AB = [[int(_) - 1 for _ in input().split()] for _ in range(M)]

G = [[0] * N for _ in range(N)]
for ab in AB:
    G[ab[0]][ab[1]] = 1
    G[ab[1]][ab[0]] = 1


def dfs(Gn, a, x):
    a[x] = 1
    for i in range(N):
        if Gn[x][i] == 1 and a[i] == 0:
            a[i] = 1
            dfs(Gn, a, i)


count = 0
for i in range(N - 1):
    for j in range(i + 1, N):
        if G[i][j] == 1:
            Gn = copy.deepcopy(G)
            Gn[i][j] = 0
            Gn[j][i] = 0
            flag = False
            for k in range(N):
                a = [0] * N
                dfs(Gn, a, k)
                if all(a):
                    flag = True
                    break
            if not flag:
                count += 1
print(count)


# Optimized solution
import copy
N, M = [int(_) for _ in input().split()]
AB = [[int(_) - 1 for _ in input().split()] for _ in range(M)]

G = [set() for _ in range(N)]
a = set()
for ab in AB:
    G[ab[0]].add(ab[1])


def find(a, x):
    if a[x] != x:
        a[x] = find(a, a[x])
    return a[x]


def unite(a, x, y):
    x = find(a, x)
    y = find(a, y)
    if x != y:
        a[x] = min(x, y)
        a[y] = a[x]


ans = 0
for i in range(N - 1):
    for j in range(i + 1, N):
        if j in G[i]:
            Gn = copy.deepcopy(G)
            Gn[i].discard(j)
            a = [i for i in range(N)]
            for k in range(N - 1):
                for l in Gn[k]:
                    unite(a, k, l)
            par = set()
            for k in range(N):
                par.add(find(a, k))
            if len(par) > 1:
                ans += 1
print(ans)
# Original solution
N, M = [int(a) for a in input().split()]
adj = [[False] * N for _ in range(N)]
edgelist = []
for i in range(M):
    a, b = [int(a) for a in input().split()]
    a-=1
    b-=1
    adj[a][b] = True
    adj[b][a] = True
    edgelist.append((a,b))

bridges = 0
for a,b in edgelist:
    adj[a][b] = False
    adj[b][a] = False

    marked = [False] * N
    stk = [a]
    while stk:
        x = stk.pop()
        marked[x] = True
        for i in range(N):
            if adj[x][i] and not marked[i]:
                stk.append(i)
    if not marked[b]:
        bridges += 1

    adj[a][b] = True
    adj[b][a] = True

print(bridges)

# Optimized solution
def f(a, b, nodes, visited):
    if (a in visited):
        return False
    if (a == b):
        return True
    visited.append(a)
    for e in nodes[a]:
        if (f(e, b, nodes, visited)):
            return True
    return False


def main():
    n, m = [int(x) for x in input().split(' ')]
    nodes = {}
    edges = []
    for _ in range(m):
        a, b = [int(x) for x in input().split(' ')]
        if (a in nodes):
            nodes[a].append(b)
        else:
            nodes[a] = [b]
        if (b in nodes):
            nodes[b].append(a)
        else:
            nodes[b] = [a]
        edges.append((a, b))
    ans = 0
    for a, b in edges:
        nodes[a] = [x for x in nodes[a] if x != b]
        nodes[b] = [x for x in nodes[b] if x != a]
        if (not f(a, b, nodes, [])):
            ans += 1
        nodes[a].append(b)
        nodes[b].append(a)
    print(ans)


main()