Problem:
Score : 600 points

Problem Statement

There are N dots in a two-dimensional plane. The coordinates of the i-th dot are (x_i, y_i).

We will repeat the following operation as long as possible:

Choose four integers a, b, c, d (a \neq c, b \neq d) such that there are dots at exactly three of the positions (a, b), (a, d), (c, b) and (c, d), and add a dot at the remaining position.

We can prove that we can only do this operation a finite number of times. Find the maximum number of times we can do the operation.

Constraints

1 \leq N \leq 10^5

1 \leq x_i, y_i \leq 10^5

If i \neq j, x_i \neq x_j or y_i \neq y_j.

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N
x_1 y_1
:
x_N y_N

Output

Print the maximum number of times we can do the operation.

Sample Input 1

3
1 1
5 1
5 5

Sample Output 1

1

By choosing a = 1, b = 1, c = 5, d = 5, we can add a dot at (1, 5). We cannot do the operation any more, so the maximum number of operations is 1.

Sample Input 2

2
10 10
20 20

Sample Output 2

0

There are only two dots, so we cannot do the operation at all.

Sample Input 3

9
1 1
2 1
3 1
4 1
5 1
1 2
1 3
1 4
1 5

Sample Output 3

16

We can do the operation for all choices of the form a = 1, b = 1, c = i, d = j (2 \leq i,j \leq 5), and no more. Thus, the maximum number of operations is 16.

Solution in Rust (1.15.1) (judge verdict: Accepted)

#![allow(unused_imports)]
#![allow(non_snake_case)]
use std::cmp::*;
use std::collections::*;
use std::io::Write;

#[allow(unused_macros)]
macro_rules! debug {
    ($($e:expr),*) => {
        #[cfg(debug_assertions)]
        $({
            let (e, mut err) = (stringify!($e), std::io::stderr());
            writeln!(err, "{} = {:?}", e, $e).unwrap()
        })*
    };
}

fn make_compress_dict(xs: &Vec<usize>) -> HashMap<usize, usize> {
    let mut x_compress_dict = HashMap::new();
    for &x in xs.iter() {
        if !x_compress_dict.contains_key(&x) {
            let next = x_compress_dict.len();
            x_compress_dict.insert(x, next);
        }
    }
    x_compress_dict
}

fn main() {
    let n = read::<usize>();

    let mut points = vec![];
    for _ in 0..n {
        let v = read_vec::<usize>();
        let (x, y) = (v[0], v[1]);
        points.push((x, y));
    }
    points.sort();

    let mut xs = points.iter().map(|x| x.0).collect::<Vec<_>>();
    let mut ys = points.iter().map(|x| x.1).collect::<Vec<_>>();
    let x_compress_dict = make_compress_dict(&xs);
    let y_compress_dict = make_compress_dict(&ys);

    let length = x_compress_dict.len() + y_compress_dict.len();
    let mut edges = vec![vec![]; length];
    for &(x, y) in points.iter() {
        edges[x_compress_dict[&x]].push(x_compress_dict.len() + y_compress_dict[&y]);
        edges[x_compress_dict.len() + y_compress_dict[&y]].push(x_compress_dict[&x]);
    }

    let mut checked = vec![false; length];
    let mut ans = 0;
    for &(x, y) in points.iter() {
        let x = x_compress_dict[&x];
        if checked[x] {
            continue;
        }
        let (ret, x_size, y_size) = dfs(x, x_compress_dict.len(), &edges, &mut checked);
        ans += x_size * y_size - ret / 2;
    }
    println!("{}", ans);
}

fn dfs(
    current_idx: usize,
    x_length: usize,
    edges: &Vec<Vec<usize>>,
    checked: &mut Vec<bool>,
) -> (i64, i64, i64) {
    checked[current_idx] = true;
    let mut ret = 0i64;
    let mut x_size = 0i64;
    let mut y_size = 0i64;
    if current_idx < x_length {
        x_size = 1;
    } else {
        y_size = 1;
    }
    ret += edges[current_idx].len() as i64;
    for &to in edges[current_idx].iter() {
        if checked[to] {
            continue;
        }
        let result = dfs(to, x_length, edges, checked);
        ret += result.0;
        x_size += result.1;
        y_size += result.2;
    }
    (ret, x_size, y_size)
}

fn read<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>()
        .split_whitespace()
        .map(|e| e.parse().ok().unwrap())
        .collect()
}