import os

import sys

from collections import defaultdict



if os.getenv("LOCAL"):

    sys.stdin = open("_in.txt", "r")



sys.setrecursionlimit(2147483647)

INF = float("inf")



N = int(sys.stdin.readline())

X, Y = list(zip(*[list(map(int, sys.stdin.readline().split())) for _ in range(N)]))



yx = defaultdict(lambda: set())

xy = defaultdict(lambda: set())

for x, y in zip(X, Y):

    xy[x].add(y)

    yx[y].add(x)





def connected(x=None, y=None, xret=None, yret=None):

    xret = xret if xret else set()

    yret = yret if yret else set()



    if x is not None:

        for y in xy[x]:

            if y in yret:

                continue

            yret.add(y)

            xret, yret = connected(y=y, xret=xret, yret=yret)

    if y is not None:

        for x in yx[y]:

            if x in xret:

                continue

            xret.add(x)

            xret, yret = connected(x=x, xret=xret, yret=yret)



    return xret, yret





x_counted = set()

y_counted = set()



# X と Y の二部グラフで、連結であるすべての組に辺を張る

ans = 0

for x in list(xy.keys()):

    if x in x_counted:

        continue

    xs, ys = connected(x=x)

    # 辺数

    ans += len(xs) * len(ys)

    x_counted |= xs

    y_counted |= ys

# すでにある辺が重複してる

ans -= N

print(ans)

"""

https://atcoder.jp/contests/abc131/submissions/6086380

"""

import os

import sys



import numpy as np

from scipy.sparse import csr_matrix

from scipy.sparse.csgraph import connected_components



if os.getenv("LOCAL"):

    sys.stdin = open("_in.txt", "r")



sys.setrecursionlimit(2147483647)

INF = float("inf")



N = int(sys.stdin.readline())

X, Y = list(zip(*[list(map(int, sys.stdin.readline().split())) for _ in range(N)]))

PAD = 10 ** 5 + 1

X = np.array(X)

Y = np.array(Y) + PAD



graph = csr_matrix(([True] * N, (X, Y)), shape=(PAD * 2, PAD * 2))

# i から j までのパスがあれば components[i] と components[j] が同じ値

_, components = connected_components(graph)



# 同じ連結成分に含まれる X の数

# x_cnt[i]: 連結成分 i に含まれる x の数

x_cnt = np.bincount(components[:PAD], minlength=PAD * 2)

y_cnt = np.bincount(components[PAD:], minlength=PAD * 2)

# 同じ連結成分である x の数と y の数をかけた数だけその間に辺を張れる

# すでにある分は引く

print(((x_cnt * y_cnt).sum() - N))

import os
import sys
from collections import defaultdict

if os.getenv("LOCAL"):
    sys.stdin = open("_in.txt", "r")
sys.setrecursionlimit(2147483647)
INF = float("inf")
N = int(sys.stdin.readline())
X, Y = list(zip(*[list(map(int, sys.stdin.readline().split())) for _ in range(N)]))
yx = defaultdict(lambda: set())
xy = defaultdict(lambda: set())
for x, y in zip(X, Y):
    xy[x].add(y)
    yx[y].add(x)


def connected(x=None, y=None, xret=None, yret=None):
    xret = xret if xret else set()
    yret = yret if yret else set()
    if x is not None:
        for y in xy[x]:
            if y in yret:
                continue
            yret.add(y)
            xret, yret = connected(y=y, xret=xret, yret=yret)
    if y is not None:
        for x in yx[y]:
            if x in xret:
                continue
            xret.add(x)
            xret, yret = connected(x=x, xret=xret, yret=yret)
    return xret, yret


x_counted = set()
y_counted = set()
# X と Y の二部グラフで、連結であるすべての組に辺を張る
ans = 0
for x in list(xy.keys()):
    if x in x_counted:
        continue
    xs, ys = connected(x=x)
    # 辺数
    ans += len(xs) * len(ys)
    x_counted |= xs
    y_counted |= ys
# すでにある辺が重複してる
ans -= N
print(ans)

"""
https://atcoder.jp/contests/abc131/submissions/6086380
"""
import os
import sys
import numpy as np
from scipy.sparse import csr_matrix
from scipy.sparse.csgraph import connected_components

if os.getenv("LOCAL"):
    sys.stdin = open("_in.txt", "r")
sys.setrecursionlimit(2147483647)
INF = float("inf")
N = int(sys.stdin.readline())
X, Y = list(zip(*[list(map(int, sys.stdin.readline().split())) for _ in range(N)]))
PAD = 10**5 + 1
X = np.array(X)
Y = np.array(Y) + PAD
graph = csr_matrix(([True] * N, (X, Y)), shape=(PAD * 2, PAD * 2))
# i から j までのパスがあれば components[i] と components[j] が同じ値
_, components = connected_components(graph)
# 同じ連結成分に含まれる X の数
# x_cnt[i]: 連結成分 i に含まれる x の数
x_cnt = np.bincount(components[:PAD], minlength=PAD * 2)
y_cnt = np.bincount(components[PAD:], minlength=PAD * 2)
# 同じ連結成分である x の数と y の数をかけた数だけその間に辺を張れる
# すでにある分は引く
print(((x_cnt * y_cnt).sum() - N))

+""" +https://atcoder.jp/contests/abc131/submissions/6086380 +""" -from collections import defaultdict +import numpy as np +from scipy.sparse import csr_matrix +from scipy.sparse.csgraph import connected_components -yx = defaultdict(lambda: set()) -xy = defaultdict(lambda: set()) -for x, y in zip(X, Y): -    xy[x].add(y) -    yx[y].add(x) - - -def connected(x=None, y=None, xret=None, yret=None): -    xret = xret if xret else set() -    yret = yret if yret else set() -    if x is not None: -        for y in xy[x]: -            if y in yret: -                continue -            yret.add(y) -            xret, yret = connected(y=y, xret=xret, yret=yret) -    if y is not None: -        for x in yx[y]: -            if x in xret: -                continue -            xret.add(x) -            xret, yret = connected(x=x, xret=xret, yret=yret) -    return xret, yret - - -x_counted = set() -y_counted = set() -# X と Y の二部グラフで、連結であるすべての組に辺を張る -ans = 0 -for x in list(xy.keys()): -    if x in x_counted: -        continue -    xs, ys = connected(x=x) -    # 辺数 -    ans += len(xs) * len(ys) -    x_counted |= xs -    y_counted |= ys -# すでにある辺が重複してる -ans -= N -print(ans) +PAD = 10**5 + 1 +X = np.array(X) +Y = np.array(Y) + PAD +graph = csr_matrix(([True] * N, (X, Y)), shape=(PAD * 2, PAD * 2)) +# i から j までのパスがあれば components[i] と components[j] が同じ値 +_, components = connected_components(graph) +# 同じ連結成分に含まれる X の数 +# x_cnt[i]: 連結成分 i に含まれる x の数 +x_cnt = np.bincount(components[:PAD], minlength=PAD * 2) +y_cnt = np.bincount(components[PAD:], minlength=PAD * 2) +# 同じ連結成分である x の数と y の数をかけた数だけその間に辺を張れる +# すでにある分は引く +print(((x_cnt * y_cnt).sum() - N))