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 Fortran (gfortran v4.8.4) (judge verdict: Accepted)

module mod_t_item
  implicit none
  type :: t_item
    integer :: pos
  end type t_item
  private
  public :: t_item, itemize
contains
  function itemize(pos) result(i)
    implicit none
    integer :: pos
    type(t_item) :: i
    i%pos = pos
    return
  end function itemize
end module mod_t_item
module mod_t_list
  use mod_t_item
  implicit none
  type :: t_list
    integer :: vol
    type(t_item), pointer :: arr(:)
  end type t_list
  private
  public :: t_list, init_list, release_list, push_back
  public :: pop_back, get_at, size_of_list
contains
  subroutine init_list(list)
    implicit none
    type(t_list), intent(inout) :: list
    allocate(list%arr(1))
    list%vol = 0
    return
  end subroutine init_list
  subroutine release_list(list)
    implicit none
    type(t_list), intent(inout) :: list
    if (associated(list%arr)) deallocate(list%arr)
    return
  end subroutine release_list
  subroutine push_back(list,item)
    implicit none
    type(t_list), intent(inout) :: list
    type(t_item), intent(in) :: item
    type(t_item), allocatable :: tmp(:)
    if (size(list%arr).eq.list%vol) then
      allocate(tmp(list%vol))
      tmp = list%arr
      deallocate(list%arr)
      allocate(list%arr(2*list%vol))
      list%arr(1:list%vol) = tmp
      deallocate(tmp)
    end if
    list%vol = list%vol+1
    list%arr(list%vol) = item
    return
  end subroutine push_back
  function pop_back(list) result(item)
    implicit none
    type(t_list), intent(inout) :: list
    type(t_item) :: item
    item = list%arr(list%vol)
    list%vol = list%vol-1
    return
  end function pop_back
  function get_at(list,i) result(item)
    implicit none
    type(t_list), intent(in) :: list
    integer, intent(in) :: i
    type(t_item) :: item
    item = list%arr(i)
    return
  end function get_at
  function size_of_list(list) result(s)
    implicit none
    type(t_list), intent(in) :: list
    integer :: s
    s = list%vol
    return
  end function size_of_list
end module mod_t_list
module mod_t_graph
  use mod_t_item
  use mod_t_list
  implicit none
  logical, allocatable :: used(:)
  type :: t_graph
    type(t_list), pointer :: edges(:)
  end type t_graph
  private
  public :: used
  public :: t_graph, init_graph, release_graph, add_edge, add, dfs
contains
  subroutine init_graph(graph,n)
    implicit none
    type(t_graph), intent(inout) :: graph
    integer, intent(in) :: n
    integer :: i
    allocate(used(n))
    used = .false.
    allocate(graph%edges(n))
    do i = 1, n
      call init_list(graph%edges(i))
    end do
    return
  end subroutine init_graph
  subroutine release_graph(graph)
    implicit none
    type(t_graph), intent(inout) :: graph
    integer :: n, i
    deallocate(used)
    n = size(graph%edges)
    do i = 1, n
      call release_list(graph%edges(i))
    end do
    deallocate(graph%edges)
    return
  end subroutine release_graph
  subroutine add_edge(graph,i,edge)
    implicit none
    type(t_graph), intent(inout) :: graph
    type(t_item), intent(in) :: edge
    integer, intent(in) :: i
    call push_back(graph%edges(i),edge)
    return
  end subroutine add_edge
  subroutine add(graph,i,pos)
    implicit none
    type(t_graph), intent(inout) :: graph
    integer, intent(in) :: i, pos
    call push_back(graph%edges(i),itemize(pos))
    return
  end subroutine add
  recursive subroutine dfs(graph,v,x,y)
    implicit none
    type(t_graph), intent(inout) :: graph
    type(t_item) :: e
    integer :: v, x, y, l, i
    if (used(v)) return
    used(v) = .true.
    if (v.gt.100000) then
      y = y+1
    else
      x = x+1
    end if
    l = size_of_list(graph%edges(v))
    if (l.eq.0) return
    do i = 1, l
      e = get_at(graph%edges(v),i)
      call dfs(graph,e%pos,x,y)
    end do
    return
  end subroutine dfs
end module mod_t_graph
program must_be_rectangular
  use mod_t_graph
  implicit none
  type(t_graph) :: graph
  integer :: n, x, y, i
  integer(8) :: m
  call init_graph(graph,200000)
  read(*,*) n
  do i = 1, n
    read(*,*) x, y
    call add(graph,x,100000+y)
    call add(graph,100000+y,x)
  end do
  m = 0_8
  do i = 1, 200000
    if (used(i)) cycle
    x = 0
    y = 0
    call dfs(graph,i,x,y)
    m = m+int(x,8)*int(y,8)
  end do
  write(*,'(i0)') m-int(n,8)
  call release_graph(graph)
  stop
end program must_be_rectangular