class UnionFind():

    def __init__(self, n):

        self.n = n

        self.root = [-1]*(n+1)

        self.rnk = [0]*(n+1)



    def Find_Root(self, x):

        if(self.root[x] < 0):

            return x

        else:

            self.root[x] = self.Find_Root(self.root[x])

            return self.root[x]

    def Unite(self, x, y):

        x = self.Find_Root(x)

        y = self.Find_Root(y)

        if(x == y):

            return 

        elif(self.rnk[x] > self.rnk[y]):

            self.root[x] += self.root[y]

            self.root[y] = x



        else:

            self.root[y] += self.root[x]

            self.root[x] = y

            if(self.rnk[x] == self.rnk[y]):

                self.rnk[y] += 1

    def isSameGroup(self, x, y):

        return self.Find_Root(x) == self.Find_Root(y)



    def Count(self, x):

        return -self.root[self.Find_Root(x)]



m = 100010

uf = UnionFind(m*2)

n = int(eval(input()))

for i in range(n):

    x,y = list(map(int,input().split()))

    y = y+m

    uf.Unite(x,y)



lx = {}

ly = {}

ans = 0

for i in range(m):

    rx = uf.Find_Root(i)

    lx[rx] = lx.get(rx,0)+1

    ry = uf.Find_Root(i+m)

    ly[ry] = ly.get(ry,0)+1

for i in range(m*2):

    ans += lx.get(i,0)*ly.get(i,0)



print((ans-n))
import sys

sys.setrecursionlimit(10**5)

m = 100005

visited = [False]*(m*2)

cnt = [0,0]

def dfs(i):

    visited[i] = True

    cnt[i//m] = cnt[i//m]+1

    for j in to[i]:

        if not visited[j]:

            dfs(j)

 

input = sys.stdin.readline

n = int(eval(input()))

to = [[] for _ in range(m*2)]

for _ in range(n):

    x,y = list(map(int,input().split()))

    y = y+m

    to[x].append(y)

    to[y].append(x)

 

ans = 0

for i in range(m):

    if not visited[i]:

        cnt = [0,0]

        dfs(i)

        ans = ans + cnt[0]*cnt[1]

 

print((ans-n))
class UnionFind:
    def __init__(self, n):
        self.n = n
        self.root = [-1] * (n + 1)
        self.rnk = [0] * (n + 1)

    def Find_Root(self, x):
        if self.root[x] < 0:
            return x
        else:
            self.root[x] = self.Find_Root(self.root[x])
            return self.root[x]

    def Unite(self, x, y):
        x = self.Find_Root(x)
        y = self.Find_Root(y)
        if x == y:
            return
        elif self.rnk[x] > self.rnk[y]:
            self.root[x] += self.root[y]
            self.root[y] = x
        else:
            self.root[y] += self.root[x]
            self.root[x] = y
            if self.rnk[x] == self.rnk[y]:
                self.rnk[y] += 1

    def isSameGroup(self, x, y):
        return self.Find_Root(x) == self.Find_Root(y)

    def Count(self, x):
        return -self.root[self.Find_Root(x)]


m = 100010
uf = UnionFind(m * 2)
n = int(eval(input()))
for i in range(n):
    x, y = list(map(int, input().split()))
    y = y + m
    uf.Unite(x, y)
lx = {}
ly = {}
ans = 0
for i in range(m):
    rx = uf.Find_Root(i)
    lx[rx] = lx.get(rx, 0) + 1
    ry = uf.Find_Root(i + m)
    ly[ry] = ly.get(ry, 0) + 1
for i in range(m * 2):
    ans += lx.get(i, 0) * ly.get(i, 0)
print((ans - n))

import sys

sys.setrecursionlimit(10**5)
m = 100005
visited = [False] * (m * 2)
cnt = [0, 0]


def dfs(i):
    visited[i] = True
    cnt[i // m] = cnt[i // m] + 1
    for j in to[i]:
        if not visited[j]:
            dfs(j)


input = sys.stdin.readline
n = int(eval(input()))
to = [[] for _ in range(m * 2)]
for _ in range(n):
    x, y = list(map(int, input().split()))
    y = y + m
    to[x].append(y)
    to[y].append(x)
ans = 0
for i in range(m):
    if not visited[i]:
        cnt = [0, 0]
        dfs(i)
        ans = ans + cnt[0] * cnt[1]
print((ans - n))

-class UnionFind: -    def __init__(self, n): -        self.n = n -        self.root = [-1] * (n + 1) -        self.rnk = [0] * (n + 1) +import sys -    def Find_Root(self, x): -        if self.root[x] < 0: -            return x -        else: -            self.root[x] = self.Find_Root(self.root[x]) -            return self.root[x] - -    def Unite(self, x, y): -        x = self.Find_Root(x) -        y = self.Find_Root(y) -        if x == y: -            return -        elif self.rnk[x] > self.rnk[y]: -            self.root[x] += self.root[y] -            self.root[y] = x -        else: -            self.root[y] += self.root[x] -            self.root[x] = y -            if self.rnk[x] == self.rnk[y]: -                self.rnk[y] += 1 - -    def isSameGroup(self, x, y): -        return self.Find_Root(x) == self.Find_Root(y) - -    def Count(self, x): -        return -self.root[self.Find_Root(x)] +sys.setrecursionlimit(10**5) +m = 100005 +visited = [False] * (m * 2) +cnt = [0, 0] -m = 100010 -uf = UnionFind(m * 2) +def dfs(i): +    visited[i] = True +    cnt[i // m] = cnt[i // m] + 1 +    for j in to[i]: +        if not visited[j]: +            dfs(j) + + +input = sys.stdin.readline -for i in range(n): +to = [[] for _ in range(m * 2)] +for _ in range(n): -    uf.Unite(x, y) -lx = {} -ly = {} +    to[x].append(y) +    to[y].append(x) -    rx = uf.Find_Root(i) -    lx[rx] = lx.get(rx, 0) + 1 -    ry = uf.Find_Root(i + m) -    ly[ry] = ly.get(ry, 0) + 1 -for i in range(m * 2): -    ans += lx.get(i, 0) * ly.get(i, 0) +    if not visited[i]: +        cnt = [0, 0] +        dfs(i) +        ans = ans + cnt[0] * cnt[1]