//#pragma warning(disable:4996)
//#include <Windows.h>
#include <iostream>
#include <vector>
#include <limits.h>
#include <algorithm>
#include <string>
#include <math.h>
#include <limits.h>
#include <queue>
#include <map>
#include <set>
#include <iomanip>
#include <bitset>
#include <cassert>
#include <random>
#include <functional>
#include <stack>
#include <iomanip>
#include <cassert>
//#include <boost/multiprecision/cpp_int.hpp>
#include <complex>
#include <cstdio>
#include <list>
#include <bitset>
//#include <stdio.h>

//< in.txt > out.txt
using namespace std;
//std::ios::sync_with_stdio(false);
//std::cin.tie(0);
const long long MOD = 1e9 + 7;
const long long INF = 1e18;
typedef long long LL;
typedef long double LD;
//typedef boost::multiprecision::cpp_int bigint;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PI;
typedef pair<LD, LL> pdl;
typedef pair<LD, LD> pdd;
typedef vector<LL> VLL;
typedef vector<VLL> VVLL;
typedef vector<int> VI;
typedef vector<vector<int>> VVI;
typedef unsigned long long ULL;
template<class T>
using pqueue = priority_queue<T, vector<T>, function<bool(T, T)>>;

template<class T>
inline void chmin(T& a, T b) {
	a = min(a, b);
}

template<class T>
inline void chmax(T& a, T b) {
	a = max(a, b);
}

void input();
void solve();

void daminput();
void naive();

void outputinput();

int main() {
	std::cin.tie(0);
	std::ios::sync_with_stdio(false);
	input();
	//daminput();
	solve();
	//naive();
	//outputinput();
	return 0;
}

//////////////////////////////////////////////////
//////////////////////////////////////////////////

int N, M;
VI A, B;

void input() {
	cin >> N >> M;
	A.resize(M);
	B.resize(M);
	for (int m = 0; m < M; m++) {
		cin >> A[m] >> B[m];
	}
}

void daminput() {
}

template<class T>
struct Gedge {
	int src, to;
	T cost;
	int id;
	Gedge(int s, int t, T c, int i = -1) :src(s), to(t), cost(c), id(i) {}
	Gedge(int t, T c) :src(-1), to(t), cost(c), id(-1) {}
};

template<class T>
using WeightedGraph = vector<vector<Gedge<T>>>;
using UnweightedGraph = vector<vector<LL>>;
template<class T>
using Gedges = vector<Gedge<T>>;

template<class T>
struct TNode {
	int parent;
	VI childs;
	T cost;
	int id;
	TNode() :parent(-1), cost(0), id(-1) {};
};
template<class T>
using Tree = vector<TNode<T>>;

struct DFSTreeNode {
	VLL childs;
	LL ord, lowlink;
	LL dece;   //何番目の二重連結成分に含まれるか
	DFSTreeNode() : ord(-1), lowlink(-1), dece(-1) {};
};
typedef vector<DFSTreeNode> DFSTree;

void BridgeDECEdfs(UnweightedGraph& G, DFSTree& tree, LL n, LL& ord, VVLL& backedges, LL parent) {
	if (tree[n].ord != -1)return;
	tree[n].ord = ord++;
	tree[n].lowlink = tree[n].ord;
	for (LL next : G[n]) {
		if (tree[next].ord != -1) {
			//後退辺の場合
			if (next == parent)continue;
			backedges[next].push_back(n);
			tree[n].lowlink = min(tree[n].lowlink, tree[next].ord);
		}
		else {
			tree[n].childs.push_back(next);
			BridgeDECEdfs(G, tree, next, ord, backedges, n);
			tree[n].lowlink = min(tree[n].lowlink, tree[next].lowlink);
		}
	}
}

void BridgeDECE(UnweightedGraph& G, DFSTree& tree, VVLL& bridges, VVLL& dece, LL start = 0) {
	tree.resize(G.size());
	bridges.resize(G.size());
	for (LL n = 0; n < G.size(); n++)tree[n].ord = -1;
	VVLL backedges;   //backedges[i]\ni j <==> 後退辺j->iが存在
	backedges.resize(G.size());
	LL ord = 0;   //次設定するord
	//ord,lowlink求める
	BridgeDECEdfs(G, tree, start, ord, backedges, -1);
	//連結要素id
	LL id = 0;
	stack<LL> st;
	for (LL n = 0; n < G.size(); n++) {
		if (tree[n].dece != -1)continue;
		st.push(n);
		dece.push_back(VLL());
		while (!st.empty()) {
			LL cur = st.top();
			st.pop();
			if (tree[cur].dece != -1)continue;
			tree[cur].dece = id;
			dece.back().push_back(cur);
			//子
			for (LL ch : tree[cur].childs) {
				if (tree[cur].ord < tree[ch].lowlink) {
					//橋だった
					bridges[cur].push_back(ch);
				}
				else {
					st.push(ch);
				}
			}
			//後退辺
			for (LL next : backedges[cur]) {
				st.push(next);
			}
		}
		id++;
	}
}

void JointPoint(DFSTree& tree, VLL& jpoints, LL start = 0) {
	if (tree[start].childs.size() > 1) {
		jpoints.push_back(start);
	}
	//関節点の条件:
	//point v is a jointpoint
	// <==> \exist u \in v.childs,u.ord <= v.lowlink
	for (LL n = 0; n < tree.size(); n++) {
		if (n == start)continue;
		for (LL ch : tree[n].childs) {
			if (tree[ch].lowlink >= tree[n].ord) {
				jpoints.push_back(n);
				break;
			}
		}
	}
}

void solve() {
	UnweightedGraph G(N);
	for (int m = 0; m < M; m++) {
		G[A[m] - 1].push_back(B[m] - 1);
		G[B[m] - 1].push_back(A[m] - 1);
	}
	DFSTree dfstree;
	VVLL bridges;
	VVLL dece;
	BridgeDECE(G, dfstree, bridges, dece);
	int ans = 0;
	for (int n = 0; n < N; n++) {
		ans += bridges[n].size();
	}
	cout << ans << "\n";
}

void naive() {
}

void outputinput() {
}

Measured memory_bytes: 3216