# (x,a), (x,b) → (s,a)と(t,b)は同値になる → aとbを同一視

# 連結成分を作る

N = int(eval(input()))

U = 10 ** 5

root_x = [None] * (U+1)

root_y = [None] * (U+1)

size_x = [0] * (U+1) # 連結成分のxの個数

size_y = [0] * (U+1) # 連結成分のyの個数

cnt_x = [0] * (U+1) # 生成時に登場した点

cnt_y = [0] * (U+1) # 生成時に登場した点

x_to_y = [None] * (U+1)

y_to_x = [None] * (U+1)



def find_root(root,size,x):

  y = root[x]

  if y is None:

    root[x] = x

    size[x] = 1

    return x

  if x == y:

    return y

  z = find_root(root,size,y)

  root[x] = z

  return z



def merge(root,size,cnt,x,y):

  rx = find_root(root,size,x)

  ry = find_root(root,size,y)

  if rx == ry:

    return

  sx = size[rx]

  sy = size[ry]

  if sx > sy:

    root[ry] = rx

    size[rx] += sy

    cnt[rx] += cnt[ry]

  else:

    root[rx] = ry

    size[ry] += sx

    cnt[ry] += cnt[rx]

  return



for _ in range(N):

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

  rx = find_root(root_x,size_x,x)

  ry = find_root(root_y,size_y,y)

  cnt_x[rx] += 1

  cnt_y[ry] += 1

  if x_to_y[rx] is None:

    x_to_y[rx] = ry

  if y_to_x[ry] is None:

    y_to_x[ry] = rx

  merge(root_y, size_y, cnt_y, x_to_y[rx], ry)

  merge(root_x, size_x, cnt_x, y_to_x[ry], rx)

  x_to_y[root_x[rx]] = root_y[ry]

  y_to_x[root_y[ry]] = root_x[rx]



  

answer = 0



for x in range(1,U+1):

  if root_x[x] is None:

    continue

  rx = find_root(root_x,size_x,x)

  if x != rx:

    continue

  y = x_to_y[x]

  ry = find_root(root_y,size_y,y)

  sx = size_x[rx]

  sy = size_y[ry]

  cnt = cnt_x[rx]

  answer += (sx * sy - cnt)

print(answer)

from collections import defaultdict

U = 10 ** 5

root = [None] * (2*U+1)

N = int(eval(input()))

XY = [[int(x) for x in input().split()] for _ in range(N)]



def find_root(x):

  y = root[x]

  if y is None:

    root[x] = x

    return x

  if x == y:

    return y

  z = find_root(y)

  root[x] = z

  return z



def merge(x,y):

  rx = find_root(x)

  ry = find_root(y)

  if rx == ry:

    return

  root[ry] = rx

  return



for x,y in XY:

  merge(x,U+y)



pts_cnt = defaultdict(int)

x_cnt = defaultdict(int)

y_cnt = defaultdict(int)



for x in range(U+1):

  if root[x] is None:

    continue

  x_cnt[find_root(x)] += 1



for y in range(U+1,U+U+1):

  if root[y] is None:

    continue

  y_cnt[find_root(y)] += 1



for x,y in XY:

  pts_cnt[find_root(x)] += 1



answer = 0

for k,v in list(pts_cnt.items()):

  answer += x_cnt[k] * y_cnt[k] - v



print(answer)
# (x,a), (x,b) → (s,a)と(t,b)は同値になる → aとbを同一視
# 連結成分を作る
N = int(eval(input()))
U = 10**5
root_x = [None] * (U + 1)
root_y = [None] * (U + 1)
size_x = [0] * (U + 1)  # 連結成分のxの個数
size_y = [0] * (U + 1)  # 連結成分のyの個数
cnt_x = [0] * (U + 1)  # 生成時に登場した点
cnt_y = [0] * (U + 1)  # 生成時に登場した点
x_to_y = [None] * (U + 1)
y_to_x = [None] * (U + 1)


def find_root(root, size, x):
    y = root[x]
    if y is None:
        root[x] = x
        size[x] = 1
        return x
    if x == y:
        return y
    z = find_root(root, size, y)
    root[x] = z
    return z


def merge(root, size, cnt, x, y):
    rx = find_root(root, size, x)
    ry = find_root(root, size, y)
    if rx == ry:
        return
    sx = size[rx]
    sy = size[ry]
    if sx > sy:
        root[ry] = rx
        size[rx] += sy
        cnt[rx] += cnt[ry]
    else:
        root[rx] = ry
        size[ry] += sx
        cnt[ry] += cnt[rx]
    return


for _ in range(N):
    x, y = list(map(int, input().split()))
    rx = find_root(root_x, size_x, x)
    ry = find_root(root_y, size_y, y)
    cnt_x[rx] += 1
    cnt_y[ry] += 1
    if x_to_y[rx] is None:
        x_to_y[rx] = ry
    if y_to_x[ry] is None:
        y_to_x[ry] = rx
    merge(root_y, size_y, cnt_y, x_to_y[rx], ry)
    merge(root_x, size_x, cnt_x, y_to_x[ry], rx)
    x_to_y[root_x[rx]] = root_y[ry]
    y_to_x[root_y[ry]] = root_x[rx]
answer = 0
for x in range(1, U + 1):
    if root_x[x] is None:
        continue
    rx = find_root(root_x, size_x, x)
    if x != rx:
        continue
    y = x_to_y[x]
    ry = find_root(root_y, size_y, y)
    sx = size_x[rx]
    sy = size_y[ry]
    cnt = cnt_x[rx]
    answer += sx * sy - cnt
print(answer)

from collections import defaultdict

U = 10**5
root = [None] * (2 * U + 1)
N = int(eval(input()))
XY = [[int(x) for x in input().split()] for _ in range(N)]


def find_root(x):
    y = root[x]
    if y is None:
        root[x] = x
        return x
    if x == y:
        return y
    z = find_root(y)
    root[x] = z
    return z


def merge(x, y):
    rx = find_root(x)
    ry = find_root(y)
    if rx == ry:
        return
    root[ry] = rx
    return


for x, y in XY:
    merge(x, U + y)
pts_cnt = defaultdict(int)
x_cnt = defaultdict(int)
y_cnt = defaultdict(int)
for x in range(U + 1):
    if root[x] is None:
        continue
    x_cnt[find_root(x)] += 1
for y in range(U + 1, U + U + 1):
    if root[y] is None:
        continue
    y_cnt[find_root(y)] += 1
for x, y in XY:
    pts_cnt[find_root(x)] += 1
answer = 0
for k, v in list(pts_cnt.items()):
    answer += x_cnt[k] * y_cnt[k] - v
print(answer)

-# (x,a), (x,b) → (s,a)と(t,b)は同値になる → aとbを同一視 -# 連結成分を作る +from collections import defaultdict + +U = 10**5 +root = [None] * (2 * U + 1) -U = 10**5 -root_x = [None] * (U + 1) -root_y = [None] * (U + 1) -size_x = [0] * (U + 1)  # 連結成分のxの個数 -size_y = [0] * (U + 1)  # 連結成分のyの個数 -cnt_x = [0] * (U + 1)  # 生成時に登場した点 -cnt_y = [0] * (U + 1)  # 生成時に登場した点 -x_to_y = [None] * (U + 1) -y_to_x = [None] * (U + 1) +XY = [[int(x) for x in input().split()] for _ in range(N)] -def find_root(root, size, x): +def find_root(x): -        size[x] = 1 -    z = find_root(root, size, y) +    z = find_root(y) -def merge(root, size, cnt, x, y): -    rx = find_root(root, size, x) -    ry = find_root(root, size, y) +def merge(x, y): +    rx = find_root(x) +    ry = find_root(y) -    sx = size[rx] -    sy = size[ry] -    if sx > sy: -        root[ry] = rx -        size[rx] += sy -        cnt[rx] += cnt[ry] -    else: -        root[rx] = ry -        size[ry] += sx -        cnt[ry] += cnt[rx] +    root[ry] = rx -for _ in range(N): -    x, y = list(map(int, input().split())) -    rx = find_root(root_x, size_x, x) -    ry = find_root(root_y, size_y, y) -    cnt_x[rx] += 1 -    cnt_y[ry] += 1 -    if x_to_y[rx] is None: -        x_to_y[rx] = ry -    if y_to_x[ry] is None: -        y_to_x[ry] = rx -    merge(root_y, size_y, cnt_y, x_to_y[rx], ry) -    merge(root_x, size_x, cnt_x, y_to_x[ry], rx) -    x_to_y[root_x[rx]] = root_y[ry] -    y_to_x[root_y[ry]] = root_x[rx] +for x, y in XY: +    merge(x, U + y) +pts_cnt = defaultdict(int) +x_cnt = defaultdict(int) +y_cnt = defaultdict(int) +for x in range(U + 1): +    if root[x] is None: +        continue +    x_cnt[find_root(x)] += 1 +for y in range(U + 1, U + U + 1): +    if root[y] is None: +        continue +    y_cnt[find_root(y)] += 1 +for x, y in XY: +    pts_cnt[find_root(x)] += 1 -for x in range(1, U + 1): -    if root_x[x] is None: -        continue -    rx = find_root(root_x, size_x, x) -    if x != rx: -        continue -    y = x_to_y[x] -    ry = find_root(root_y, size_y, y) -    sx = size_x[rx] -    sy = size_y[ry] -    cnt = cnt_x[rx] -    answer += sx * sy - cnt +for k, v in list(pts_cnt.items()): +    answer += x_cnt[k] * y_cnt[k] - v