#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cmath>
#include <set>
#include <stack>
template<typename T>
class Union_find_tree {
	T *_value = nullptr;
	mutable Union_find_tree<T> *_parent = nullptr;
	const Union_find_tree<T>* parent() const {
		if (_parent == nullptr) return this;
		else return _parent = _parent->parent();
	}
	Union_find_tree<T>* parent() {
		if (_parent == nullptr) return this;
		else return _parent = _parent->parent();
	}
public:
	Union_find_tree() {};
	~Union_find_tree()
	{
		delete _value;
	}
	bool is_same_union(const Union_find_tree<T> &other) const {
		return parent() == other.parent();
	}
	void join(Union_find_tree<T> &other) {
		if (!is_same_union(other)) {
			parent()->_parent = &other;
		}
	}
	void set_value(const T &value) {
		delete (parent()->_value);
		parent()->_value = new T(value);
	}
	bool value_defined() const {
		return parent()->_value != nullptr;
	}
	T get_value() const {
		return *(parent()->_value);
	}
};

class Town {
public:
	Union_find_tree<int> uft;
	std::set<int> way_to;
	std::set<int> way_from;
};
std::vector<Town> new_network(std::vector<Town> &towns) {
	std::vector<Town> res;
	for (auto &town : towns) {
		if (!town.uft.value_defined()) {
			town.uft.set_value(res.size());
			res.push_back(Town());
		}
	}
	for (auto &town : towns) {
		auto from = town.uft.get_value();
		for (auto &to_idx : town.way_to) {
			auto to = towns[to_idx].uft.get_value();
			if (from != to) {
				res[from].way_to.insert(to);
				res[to].way_from.insert(from);
			}
		}
	}
	return res;
}
std::vector<int> cal_rank(const std::vector<Town> &towns) {
	std::stack<std::pair<int, std::set<int>::iterator>> stack;
	std::vector<int> memo(towns.size(), -1);
	int rank = 0;
	for (auto i = 0; i < memo.size(); ++i) {
		if (memo[i] == -1) {
			memo[i] = 0;
			stack.push(std::make_pair(i, towns[i].way_to.begin()));
			while (!stack.empty()) {
				auto top = stack.top();
				if (top.second == towns[top.first].way_to.end()) {
					stack.pop();
					memo[top.first] = rank++;
				}
				else {
					stack.top().second++;
					if (memo[(*top.second)] == -1) {
						memo[(*top.second)] = 0;
						stack.push(std::make_pair(*top.second, towns[*top.second].way_to.begin()));
					}
				}
			}
		}
	}
	return memo;
}
template<typename T>
struct Indexed_value {
	int index;
	T value;
	Indexed_value() : index{}, value{}{}
	Indexed_value(int index, T value) :index{ index }, value{ value }{}
};
void cal_dng(std::vector<Town> &towns) {
	auto rank = cal_rank(towns);
	std::stack<int> stack;
	std::vector<Indexed_value<int>> with_index(rank.size());
	for (auto i = 0; i < rank.size(); ++i) with_index[i] = Indexed_value<int>( i, rank[i] );
	std::sort(with_index.begin(), with_index.end(), [](const Indexed_value<int> a, const Indexed_value<int>b) {return a.value > b.value; });
	for (auto indexed_value : with_index) {
		if (rank[indexed_value.index] >= 0) {
			rank[indexed_value.index] = -1;
			stack.push(indexed_value.index);
			while (!stack.empty()) {
				auto top = stack.top();
				stack.pop();
				for (auto to : towns[top].way_from) {
					if (rank[to] != -1) {
						rank[to] = -1;
						stack.push(to);
						towns[indexed_value.index].uft.join(towns[to].uft);
					}
				}
			}
		}
	}
}
int f(std::vector<Town> &towns) {
	cal_dng(towns);
	auto new_towns = new_network(towns);
	if (new_towns.size() == 1) return 0;
	int to_end{ 0 }, from_end{ 0 };
	for (auto town : new_towns) {
		if (town.way_from.size() == 0) ++from_end;
		if (town.way_to.size() == 0) ++to_end;
	}
	return (from_end > to_end) ? from_end : to_end;
}
int main() {
	int n, m;
	std::cin >> n >> m;
	std::vector<Town> towns(n);
	for (auto i = 0; i < m; ++i) {
		int s, t;
		std::cin >> s >> t;
		towns[s].way_to.insert(t);
		towns[t].way_from.insert(s);
	}
	std::cout << f(towns) << std::endl;
}


Measured memory_bytes: 23964