#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define rep(x, s, t) for(llint (x) = (s); (x) <= (t); (x)++) #define reps(x, s) for(llint (x) = 0; (x) < (llint)(s).size(); (x)++) #define chmin(x, y) (x) = min((x), (y)) #define chmax(x, y) (x) = max((x), (y)) #define all(x) (x).begin(),(x).end() #define outl(x) cout << x << endl #define SP << " " << #define inf 1e18 using namespace std; typedef long long llint; typedef long long ll; typedef pair P; struct UnionFind{ int size; vector parent; vector rank; vector v, e; UnionFind(){} UnionFind(int size){ this->size = size; parent.resize(size+1); rank.resize(size+1); v.resize(size+1); e.resize(size+1); init(); } void init(){ for(int i = 0; i <= size; i++){ parent[i] = i, rank[i] = 0; v[i] = 1, e[i] = 0; } } int root(int i){ if(parent[i] == i) return i; return parent[i] = root(parent[i]); } bool same(int i, int j){ return root(i) == root(j); } void merge(int i, int j){ // j will become new root parent[i] = j; v[j] += v[i]; e[j] += e[i] + 1; } void unite(int i, int j){ int root_i = root(i), root_j = root(j); if(root_i == root_j){ e[root_i]++; return; } if(rank[root_i] < rank[root_j]) merge(root_i, root_j); else merge(root_j, root_i); if(rank[root_i] == rank[root_j]) rank[root_i]++; } }; ll n, m; ll b[200005], c[200005]; vector vec[200005]; UnionFind uf(200005); int main(void) { ios::sync_with_stdio(0); cin.tie(0); cin >> n >> m; rep(i, 1, n) cin >> b[i] >> c[i]; rep(i, 1, n) vec[c[i]].push_back(b[i]); ll ans = 0; rep(i, 1, n){ sort(all(vec[i])); if(vec[i].size() <= 1) continue; rep(j, 1, (int)vec[i].size()-1){ ll u = vec[i][j-1], v = vec[i][j]; if(!uf.same(u, v)) ans++, uf.unite(u, v); } } cout << ans << endl; return 0; }