Problem:
Problem D: King Slime

There is a grid of size W × H surrounded by walls. Some cells in the grid are occupied by slimes. The slimes want to unite with each other and become a "King Slime".

In each move, a slime can move to east, west, south and north direction until it enters a cell occupied by another slime or hit the surrounding wall. If two slimes come together, they unite and become a new slime.

Your task is write a program which calculates the minimum number of moves that all the slimes unite and become a King Slime. Suppose slimes move one by one and they never move simultaneously.

Input

The first line contains three integers N (2 ≤ N ≤ 40,000), W and H (1 ≤ W, H ≤ 100,000), which denote the
number of slimes, the width and the height of the grid respectively.

The following N lines describe the initial coordinates of the slimes. The i-th line contains two integers xi (1 ≤ xi ≤ W) and yi (1 ≤ yi ≤ H), which indicate the coordinates of the i-th slime . All the coordinates are 1-based.

You may assume that each cell is occupied by at most one slime initially.

Output

Output the minimum number of moves that all the slimes unite and become a King Slime.

Sample Input 1

4 3 3
1 1
1 3
3 1
3 3

Output for the Sample Input 1

3

Sample Input 2

2 3 3
2 2
3 3

Output for the Sample Input 2

2

Sample Input 3

2 4 4
2 2
3 3

Output for the Sample Input 3

3

Sample Input 4

2 4 4
2 2
2 3

Output for the Sample Input 4

1

Solution in JAVA (judge verdict: Accepted)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		solver();
	}

	@SuppressWarnings("unchecked")
	static void solver() {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int w = sc.nextInt();
		int h = sc.nextInt();
		int[] x = new int[n];
		int[] y = new int[n];
		ArrayList<Integer>[] x_ord = new ArrayList[w];
		ArrayList<Integer>[] y_ord = new ArrayList[h];
		for (int i = 0; i < w; i++)
			x_ord[i] = new ArrayList<>();
		for (int i = 0; i < h; i++)
			y_ord[i] = new ArrayList<>();
		int x0 = 0, xw = 0, y0 = 0, yh = 0;
		for (int i = 0; i < n; i++) {
			x[i] = sc.nextInt() - 1;
			y[i] = sc.nextInt() - 1;
			if (x[i] == 0 && x0 == 0)
				x0++;
			else if (x[i] == w - 1 && xw == 0)
				xw++;
			if (y[i] == 0 && y0 == 0)
				y0++;
			else if (y[i] == h - 1 && yh == 0)
				yh++;
			x_ord[x[i]].add(i);
			y_ord[y[i]].add(i);
		}
		int d = Math.max(Math.max(x0, xw), Math.max(y0, yh));

		DJSet ds = new DJSet(n);
		for (int i = 0; i < w; i++) {
			while (!x_ord[i].isEmpty()) {
				for (int j = 0; j < x_ord[i].size(); j++) {
					ds.setUnion(x_ord[i].get(0), x_ord[i].get(j));
				}
				x_ord[i].clear();
			}
		}
		for (int i = 0; i < h; i++) {
			while (!y_ord[i].isEmpty()) {
				for (int j = 0; j < y_ord[i].size(); j++) {
					ds.setUnion(y_ord[i].get(0), y_ord[i].get(j));
				}
				y_ord[i].clear();
			}
		}
		int sum = n - ds.count();
		if (ds.count() == 1) {
			System.out.println(sum);
		} else {
			sum +=2 * ds.count() - 1 - d;
			System.out.println(sum);
		}

	}

	public static class DJSet {
		int n;// the number of vertices
		int[] d;

		DJSet(int n) {
			this.n = n;
			d = new int[n];
			Arrays.fill(d, -1);
		}

		int root(int x) {
			return d[x] < 0 ? x : root(d[x]);
		}

		boolean setUnion(int x, int y) {
			x = root(x);
			y = root(y);
			if (x != y) {
				if (x < y) {
					int d = x;
					x = y;
					y = d;
				}
				// x>y
				d[y] += d[x];
				d[x] = y;
			}
			return x != y;
		}

		boolean equiv(int x, int y) {
			return root(x) == root(y);
		}

		// the number of nodes of which graph contains x
		int size(int x) {
			return d[root(x)] * (-1);
		}

		// the number of connected graphs
		int count() {
			int ct = 0;
			for (int u : d) {
				if (u < 0)
					ct++;
			}
			return ct;
		}
	}
}