#include "bits/stdc++.h"

using namespace std;

using ll = long long int;
using ld = long double;

using vi = vector<int>;
using vvi = vector<vi>;
using vvvi = vector<vvi>;
using vl = vector<ll>;
using vvl = vector<vl>;
using vvvl = vector<vvl>;
using vd = vector<double>;
using vvd = vector<vd>;
using vb = vector<bool>;
using vvb = vector<vb>;
using vs = vector<string>;

const int INF = (1 << 30) - 1;
const ll INF64 = ((ll)1 << 62) - 1;
const double PI = 3.1415926535897932384626433832795;

const int dx[] = { -1, 0, 1, 0 };
const int dy[] = { 0, 1, 0, -1 };

int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
int lcm(int x, int y) { return x / gcd(x, y) * y; }
ll lcm(ll x, ll y) { return x / gcd(x, y) * y; }

struct UnionFind
{
	vector< int > data;

	UnionFind(int sz)
	{
		data.assign(sz, -1);
	}

	bool unite(int x, int y)
	{
		x = find(x), y = find(y);
		if (x == y) return (false);
		if (data[x] > data[y]) swap(x, y);
		data[x] += data[y];
		data[y] = x;
		return (true);
	}

	int find(int k)
	{
		if (data[k] < 0) return (k);
		return (data[k] = find(data[k]));
	}

	int size(int k)
	{
		return (-data[find(k)]);
	}
};

struct LowLink
{
	UnionFind uf;
	vector< vector< int > > g;
	vector< int > used, ord, low, parent;

	LowLink(size_t v) : g(v), used(v, 0), ord(v), low(v), uf(v), parent(v, -1) {}

	virtual void add_edge(int x, int y)
	{
		g[x].push_back(y);
		g[y].push_back(x);
	}

	void dfs(int idx, int &k, int par = -1)
	{
		used[idx] = true;
		ord[idx] = k++;
		low[idx] = ord[idx];

		for (auto &to : g[idx]) {
			if (!used[to]) {
				dfs(to, k, idx);
				low[idx] = min(low[idx], low[to]);
				parent[to] = idx;
				if (ord[idx] >= low[to]) uf.unite(idx, to);
			}
			else if (to != par) {
				low[idx] = min(low[idx], ord[to]);
			}
		}
	}

	void dfs()
	{
		int k = 0;
		dfs(0, k);
	}
};

struct ArticulationPoints : LowLink
{
	ArticulationPoints(size_t v) : LowLink(v) {}

	vector< int > build()
	{
		LowLink::dfs();
		vector< int > vs;
		int sum = 0;
		for (int i = g.size() - 1; i >= 1; i--) {
			if (parent[i] == 0) ++sum;
			else if (ord[parent[i]] <= low[i]) vs.push_back(parent[i]);
		}
		if (sum > 1)vs.push_back(0);
		sort(begin(vs), end(vs));
		vs.erase(unique(begin(vs), end(vs)), end(vs));
		return (vs);
	}
};

struct BiConnectedComponents : LowLink
{
	vector< pair< int, int > > edges;
	vector< int > comp;

	BiConnectedComponents(size_t v) : LowLink(v), comp(v) {}

	void add_edge(int x, int y)
	{
		LowLink::add_edge(x, y);
		edges.push_back(minmax(x, y));
	}

	int operator[](int k)
	{
		return (comp[k]);
	}

	vector< pair< int, int > > build(vector< vector< int > > &t)
	{
		LowLink::dfs();
		int ptr = 0;
		vector< int > cc(g.size());
		for (int i = 0; i < g.size(); i++) {
			if (i == uf.find(i)) cc[i] = ptr++;
		}

		t.resize(ptr);
		for (int i = 0; i < g.size(); i++) {
			comp[i] = cc[uf.find(i)];
		}
		vector< pair< int, int > > vs;
		for (auto &e : edges) {
			int x = comp[e.first], y = comp[e.second];
			if (x == y) continue;
			vs.emplace_back(e.first, e.second);
			t[x].push_back(y);
			t[y].push_back(x);
		}
		sort(begin(vs), end(vs));
		return (vs);
	}
};

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int n, m;
	cin >> n >> m;
	BiConnectedComponents graph(n);
	vector< vector< int > > ei1333;
	for (int i = 0; i < m; i++) {
		int s, t;
		cin >> s >> t;
		s--; t--;
		graph.add_edge(s, t);
	}

	int res = 0;
	for (auto &p : graph.build(ei1333)) {
		res++;
	}

	cout << res << endl;
	return 0;
}


Measured memory_bytes: 256