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 (Python):
from collections import defaultdict


class UnionFind():
    n = 1
    par = [0]
    rnk = [0]

    def __init__(self, size):
        self.n = size
        self.par = [i for i in range(self.n)]
        self.rnk = [0 for i in range(self.n)]

    def find(self, vertex1):
        if self.par[vertex1] == vertex1:
            return vertex1
        else:
            self.par[vertex1] = self.find(self.par[vertex1])
            return self.par[vertex1]

    def unite(self, vertex1, vertex2):
        vertex1 = self.find(vertex1)
        vertex2 = self.find(vertex2)
        if vertex1 == vertex2:
            return
        if (self.rnk[vertex1] < self.rnk[vertex2]):
            self.par[vertex1] = vertex2
        else:
            self.par[vertex2] = vertex1
            if (self.rnk[vertex1] == self.rnk[vertex2]):
                self.rnk[vertex1] += 1

    def same(self, vetrex1, vertex2):
        return self.find(vetrex1) == self.find(vertex2)


N = int(input())
P = [(i,) + tuple(map(int, input().split())) for i in range(N)]
G = UnionFind(N)
P.sort(key=lambda x: x[1])
for i in range(N - 1):
    if P[i][1] == P[i + 1][1]:
        G.unite(P[i][0], P[i + 1][0])
P.sort(key=lambda x: x[2])
for i in range(N - 1):
    if P[i][2] == P[i + 1][2]:
        G.unite(P[i][0], P[i + 1][0])
P.sort(key=lambda x: x[0])
Dx = defaultdict(set)
Dy = defaultdict(set)
for i in range(N):
    j = G.find(i)
    Dx[j].add(P[i][1])
    Dy[j].add(P[i][2])
ans = -N
for j in Dx:
    ans += len(Dx[j]) * len(Dy[j])
print(ans)

Solution (Python):
import sys
from collections import deque  
from collections import defaultdict  
from heapq import heapify, heappush, heappop, heappushpop  
from bisect import bisect_left, bisect_right  


sys.setrecursionlimit(int(1e+7))  

s2nn = lambda s: [int(c) for c in s.split(' ')]
ss2nn = lambda ss: [int(s) for s in ss]
ss2nnn = lambda ss: [s2nn(s) for s in ss]
i2s = lambda: sys.stdin.readline().rstrip()
i2n = lambda: int(i2s())
i2nn = lambda: s2nn(i2s())
ii2ss = lambda n: [sys.stdin.readline().rstrip() for _ in range(n)]
ii2nn = lambda n: ss2nn(ii2ss(n))
ii2nnn = lambda n: ss2nnn(ii2ss(n))

def main():
    N = i2n()
    XY = ii2nnn(N)
    M = int(1e+5) + 5

    
    to = [[] for _ in range(M*2)]
    cnt = [0] * (M*2)
    visited = [False] * (M*2)

    
    for x, y in XY:
        y += M
        to[x].append(y)
        to[y].append(x)

    cnt = [0, 0]
    def bfs(n):
        q = deque()
        q.append(n)
        while q:
            i = q.popleft()
            if visited[i]:
                continue
            visited[i] = True
            cnt[i // M] += 1
            for j in to[i]:
                q.append(j)

    
    ans = 0
    for i in range(M*2):
        if visited[i]:
            continue
        cnt[0] = 0
        cnt[1] = 0
        bfs(i)
        ans += cnt[0] * cnt[1]
    
    ans -= N  
    print(ans)

main()

Solution (Python):
import sys
sys.setrecursionlimit(10000000)

V = 100005
N = int(input())

lines = [[] for _ in range(V * 2)]
visited = [False] * V * 2

count = []
def dfs(v):
	global count
	if visited[v]:
		return
	visited[v] = True
	count[v // V] += 1
	for u in lines[v]:
		dfs(u)


for i in range(N):
	x, y = [int(x) for x in input().split()]
	y += V
	lines[x].append(y)
	lines[y].append(x)

ans = 0
for i in range(V * 2):
	if visited[i]:
		continue
	count = [0, 0]
	dfs(i)
	ans += count[0] * count[1]

ans -= N
print(ans)

Solution (Python):
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(input())
to = [[] for _ in range(m*2)]
for _ in range(n):
    x,y = 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)

Solution (Python):
from collections import deque
N=int(input())
mod=100010
visit=[0]*(2*mod)
table=[[] for i in range(2*mod)]
for i in range(N):
    x,y=map(int,input().split())
    y+=mod
    visit[x]=1
    visit[y]=1
    table[x].append(y)
    table[y].append(x)
H=deque()
num=0
for i in range(mod):
    if visit[i]==1:
        X=0
        Y=0
        H.append(i)
        visit[i]=0
        while H:
            pt=H.popleft()
            if pt>mod:
                Y+=1
            else:
                X+=1
            for x in table[pt]:
                if visit[x]==1:
                    visit[x]=0
                    H.append(x)
        
        num+=X*Y
print(num-N)

Solution (Python):
def get_group(x):
    g = group[x]
    if g == x:
        return x
    group[g] = re = get_group(g)
    return re

def unite(x, y):
    gx = get_group(x)
    gy = get_group(y)
    if gx != gy:
        dx = depth[gx]
        dy = depth[gy]
        if dy > dx:
            gx, gy = gy, gx
        group[gy] = gx
        if dx == dy:
            depth[gx] += 1

n = int(input())
group = {}
depth = {}
edge = []
for _ in range(n):
    x, y = map(int, input().split())
    group.setdefault(x, x)
    group.setdefault(-y, -y)
    depth.setdefault(x, 1)
    depth.setdefault(-y, 1)
    edge += [[x, -y]]

for x, y in edge:
    unite(x, y)

cnt = {}
for k in group.keys():
    g = get_group(k)
    cnt.setdefault(g, [0, 0])
    if k > 0:
        cnt[g][0] += 1
    else:
        cnt[g][1] += 1

ans = 0
for cx, cy in cnt.values():
    ans += cx * cy
print(ans - n)

Solution (Python):
import sys
sys.setrecursionlimit(10**6)

def dfs(x, al):
    global cntx, cnty
    if (al, x) in used:
        return
    used.add((al, x))
    if al == 0:
        cntx += 1
    else:
        cnty += 1
    for xy in to[al][x]:
        dfs(xy, 1 - al)
    return

n = int(input())
to = [{}, {}]
for _ in range(n):
    x, y = map(int, input().split())
    to[0].setdefault(x, [])
    to[1].setdefault(y, [])
    to[0][x] += [y]
    to[1][y] += [x]
used = set()
ans = 0
for x in to[0].keys():
    if (0, x) in used: continue
    cntx = 0
    cnty = 0
    dfs(x, 0)
    ans += cntx * cnty
print(ans - n)

Solution (Python):
class UnionFind:
    def __init__(self, N):
        self.p = list(range(N))
        self.rank = [0] * N
        self.size = [1] * N

    def find_root(self, x):
        if self.p[x] != x:
            self.p[x] = self.find_root(self.p[x])

        return self.p[x]

    def same(self, x, y):
        return self.find_root(x) == self.find_root(y)

    def unite(self, x, y):
        u = self.find_root(x)
        v = self.find_root(y)

        if u == v: return

        if self.rank[u] < self.rank[v]:
            self.p[u] = v
            self.size[v] += self.size[u]
            self.size[u] = 0
        else:
            self.p[v] = u
            self.size[u] += self.size[v]
            self.size[v] = 0

            if self.rank[u] == self.rank[v]:
                self.rank[u] += 1

    def get_size(self, x):
        return self.size[self.find_root(x)]

n = int(input())
xy = [tuple(int(x) for x in input().split()) for _ in range(n)]

sx = set()
sy = set()

for x, y in xy:
    sx.add(x)
    sy.add(y)

dx = dict()
dy = dict()

for i, x in enumerate(sx):
    dx[x] = i

len_x = len(sx)
m = len(sx) + len(sy)

for i, y in enumerate(sy):
    dy[y] = i + len_x


uft = UnionFind(m)

for x, y in xy:
    uft.unite(dx[x], dy[y])

mx = [0] * m
my = [0] * m
for i in range(len_x):
    mx[uft.find_root(i)] += 1
for i in range(len_x, m):
    my[uft.find_root(i)] += 1

ans = 0
for i in range(m):
    ans += mx[i] * my[i]

print(ans - n)

Solution (Python):




N = int(input())
COR = [tuple((map(int, input().split()))) for _ in range(N)]


class Group:
    
    def __init__(self):
        self.n = 0
        self.x = 0
        self.y = 0
        self.xy = {}
        
    def new_cor_n(self):
        return self.x * self.y - self.n
        

def grouping(group, x, xcor, ycor):
    x_list = []
    group.x += 1
    for y in xcor[x][0]:
        if (x, y) not in group.xy:
            group.n += 1
            group.xy[(x, y)] = None
        if ycor[y][1]:
            continue
        ycor[y][1] = True
        group.y += 1
        for x_ in ycor[y][0]:
            if (x_, y) not in group.xy:
                group.n += 1
                group.xy[(x_, y)] = None
            if xcor[x_][1]:
                continue
            xcor[x_][1] = True
            x_list.append(x_)
    return x_list

if N <= 2:
    print(0)
else:
    xcor, ycor = {}, {}
    for xi, yi in COR:
        if xi in xcor:
            xcor[xi][0].append(yi)
        else:
            xcor[xi] = [[yi], False]
        if yi in ycor:
            ycor[yi][0].append(xi)
        else:
            ycor[yi] = [[xi], False]
    group_list = []
    for i, v in xcor.items():
        if v[1]:
            continue
        group = Group()
        group_list.append(group)
        xcor[i][1] = True
        x_list = [i]
        while True:
            new_x_list = []
            for x in x_list:
                new_x_list += grouping(group, x, xcor, ycor)
            if new_x_list == []:
                break
            x_list = new_x_list
    s = 0
    for group in group_list:
        s += group.new_cor_n()
    print(s)

Solution (Python):
import math,string,itertools,fractions,heapq,collections,re,array,bisect,sys,copy,functools

sys.setrecursionlimit(10**7)
inf = 10**20
eps = 1.0 / 10**10
mod = 998244353
dd = [(-1,0),(0,1),(1,0),(0,-1)]
ddn = [(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1)]

def LI(): return list(map(int, sys.stdin.readline().split()))
def LLI(): return [list(map(int, l.split())) for l in sys.stdin.readlines()]
def LI_(): return [int(x)-1 for x in sys.stdin.readline().split()]
def LF(): return [float(x) for x in sys.stdin.readline().split()]
def LS(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline())
def F(): return float(sys.stdin.readline())
def S(): return input()
def pf(s): return print(s, flush=True)
def pe(s): return print(str(s), file=sys.stderr)
def JA(a, sep): return sep.join(map(str, a))
def JAA(a, s, t): return s.join(t.join(map(str, b)) for b in a)


class UnionFind:
    def __init__(self, size):
        self.table = [-1 for _ in range(size)]

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

    def union(self, x, y):
        s1 = self.find(x)
        s2 = self.find(y)
        if s1 != s2:
            if self.table[s1] <= self.table[s2]:
                self.table[s1] += self.table[s2]
                self.table[s2] = s1
            else:
                self.table[s2] += self.table[s1]
                self.table[s1] = s2
            return True
        return False

    def subsetall(self):
        a = []
        for i in range(len(self.table)):
            if self.table[i] < 0:
                a.append((i, -self.table[i]))
        return a

def main():
    n = I()
    xy = [LI() for _ in range(n)]
    xd = {}
    yd = {}

    xi = 0
    yi = 0
    for x,y in xy:
        if x not in xd:
            xd[x] = xi
            xi += 1
        if y not in yd:
            yd[y] = yi
            yi += 1

    uf = UnionFind(xi+yi+n)
    ii = xi + yi
    for i in range(n):
        x,y = xy[i]
        uf.union(xd[x], ii + i)
        uf.union(xi + yd[y], ii + i)

    dx = collections.defaultdict(set)
    dy = collections.defaultdict(set)
    for i in range(n):
        x,y = xy[i]
        ui = uf.find(ii + i)
        dx[ui].add(x)
        dy[ui].add(y)

    r = -n
    for k,xv in dx.items():
        yv = dy[k]
        r += len(xv) * len(yv)

    return r


print(main())

Solution (Python):
import sys
input = sys.stdin.readline
max_n = 10**5
ans = 0
n = int(input())

class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n)]
        self.sizea = [1 if _ < n//2 else 0 for _ in range(n)]
        self.sizeb = [0 if _ < n//2 else 1 for _ in range(n)]
        self.sizec = [1 for _ in range(n)]
        self.rank = [0] * (n)
        
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    def is_root(self, x):
        if self.par[x] == x:
            return True
        else:
            return False
    
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                x, y = y, x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
            self.par[y] = x
            self.sizea[x] += self.sizea[y]
            self.sizeb[x] += self.sizeb[y]
            self.sizec[x] += self.sizec[y]
        else:
            self.sizec[x] += 1          

    def get_size(self, x):
        x = self.find(x)
        return self.sizea[x],self.sizeb[x],self.sizec[x]

uf = UnionFind(2*max_n)

for _ in range(n):
  x,y = map(int, input().split())
  x -= 1
  y += max_n - 1
  uf.union(x, y)

for i in range(max_n):
  if uf.is_root(i):
    a,b,c = uf.get_size(i)
    if a*b > 1:
      ans += a*b -(c-1)
print(ans)

Solution (Python):
import sys
from collections import defaultdict

class UnionFind:
    def __init__(self, n):
        self.root = [i for i in range(n)]
        self.rank = [0] * n

    def find(self, x):
        if self.root[x] == x:
            return x
        else:
            y = self.find(self.root[x])
            self.root[x] = y
            return self.root[x]

    def unite(self, x, y):
        if self.is_same(x, y):
            return
        rx = self.find(x)
        ry = self.find(y)
        if self.rank[rx] < self.rank[ry]:
            self.root[rx] = ry
        else:
            self.root[ry] = rx
            if self.rank[rx] == self.rank[ry]:
                self.rank[rx] += 1

    def is_same(self, x, y):
        return self.find(x) == self.find(y)
    

def main():
    input = sys.stdin.readline
    V = 10**5  
    N = int(input())
    uft = UnionFind(2*V)
    for _ in range(N):
        x, y = map(int, input().split())
        x -= 1
        y += V - 1
        uft.unite(x, y)

    cnt_x = defaultdict(lambda: 0)
    cnt_y = defaultdict(lambda: 0)
    for i in range(2*V):
        root = uft.find(i)
        if i < V:  
            cnt_x[root] += 1
        else:
            cnt_y[root] += 1

    ans = 0
    for root in cnt_x.keys():
        ans += cnt_x[root] * cnt_y[root]

    return ans - N


if __name__ == '__main__':
    print(main())

Solution (Python):
import sys
sys.setrecursionlimit(10**6)

def main():
    input = sys.stdin.readline
    V = 10**5  
    N = int(input())
    bgraph = [[] for _ in range(2*V)]
    for _ in range(N):
        x, y = map(int, input().split())
        x -= 1
        y += V - 1
        bgraph[x].append(y)
        bgraph[y].append(x)

    def dfs(i):
        if visited[i]:
            return cnt
        visited[i] = True
        cnt[i//V] += 1
        for j in bgraph[i]:
            dfs(j)
        return cnt

    visited = [False] * (2*V)
    ans = 0
    for i in range(V):
        if visited[i]:
            continue
        cnt = [0, 0]
        cnt = dfs(i)
        ans += cnt[0] * cnt[1]

    ans -= N
    return ans


if __name__ == '__main__':
    print(main())

Solution (Python):
def main():
    from collections import defaultdict
    import sys
    
    r = lambda: sys.stdin.readline().strip()
    R = lambda: list(map(int, r().split()))

    N = int(r())
    Dic = defaultdict(list)
    V = 10**5 + 5
    Arrived = [False] * (2 * V)
    for _ in range(N):
        x, y = R()
        y += V
        Dic[x].append(y)
        Dic[y].append(x)

    ans = 0
    task = []
    for k, v in Dic.items():
        if Arrived[k]:
            continue
        Arrived[k] = True
        if k < V:
            cntx = 1
            cnty = 0
        else:
            cntx = 0
            cnty = 1
        cntdot = 0
        task.extend(v)
        cntdot += len(v)
        while task:
            s = task.pop()
            if Arrived[s]:
                continue
            if s < V:
                cntx += 1
            else:
                cnty += 1
            Arrived[s] = True
            task.extend(Dic[s])
            cntdot += len(Dic[s])
        ans += cntx * cnty - cntdot // 2
    print(ans)

if __name__ == '__main__':
    main()

Solution (Python):
def main():
    from collections import defaultdict
    import sys
    
    r = lambda: sys.stdin.readline().strip()
    R = lambda: list(map(int, r().split()))

    N = int(r())
    Dic = defaultdict(list)
    V = 10**5 + 5
    Arrived = [False] * (2 * V)
    for _ in range(N):
        x, y = R()
        y += V
        Dic[x].append(y)
        Dic[y].append(x)

    ans = 0
    task = []
    for k, v in Dic.items():
        if Arrived[k]:
            continue
        Arrived[k] = True
        if k < V:
            cntx = 1
            cnty = 0
        else:
            cntx = 0
            cnty = 1
        cntdot = 0
        task.extend(v)
        cntdot += len(v)
        while task:
            s = task.pop()
            if Arrived[s]:
                continue
            if s < V:
                cntx += 1
            else:
                cnty += 1
            Arrived[s] = True
            task.extend(Dic[s])
            cntdot += len(Dic[s])
        ans += cntx * cnty - cntdot // 2
    print(ans)

if __name__ == '__main__':
    main()

Solution (Python):







import sys
sys.setrecursionlimit(10**7)
from itertools import accumulate, combinations, permutations 
from math import factorial
def factorize(n):
    """return the factors of the Arg and count of each factor
    
    Args:
        n (long): number to be resolved into factors
    
    Returns:
        list of tuples: factorize(220) returns [(2, 2), (5, 1), (11, 1)]
    """
    fct = []  
    b, e = 2, 0  
    while b * b <= n:
        while n % b == 0:
            n = n // b
            e = e + 1
        if e > 0:
            fct.append((b, e))
        b, e = b + 1, 0
    if n > 1:
        fct.append((n, 1))
    return fct
def combinations_count(n, r):
    """Return the number of selecting r pieces of items from n kinds of items.
    
    Args:
        n (long): number
        r (long): number
    
    Raises:
        Exception: not defined when n or r is negative
    
    Returns:
        long: number
    """
    
    if n < 0 or r < 0:
        raise Exception('combinations_count(n, r) not defined when n or r is negative')
    if n - r < r: r = n - r
    if r < 0: return 0
    if r == 0: return 1
    if r == 1: return n
    numerator = [n - r + k + 1 for k in range(r)]
    denominator = [k + 1 for k in range(r)]
    for p in range(2,r+1):
        pivot = denominator[p - 1]
        if pivot > 1:
            offset = (n - r) % p
            for k in range(p-1,r,p):
                numerator[k - offset] /= pivot
                denominator[k] /= pivot
    result = 1
    for k in range(r):
        if numerator[k] > 1:
            result *= int(numerator[k])
    return result
def combinations_with_replacement_count(n, r):
    """Return the number of selecting r pieces of items from n kinds of items allowing individual elements to be repeated more than once.
    
    Args:
        n (long): number
        r (long): number
    
    Raises:
        Exception: not defined when n or r is negative
    
    Returns:
        long: number
    """
    if n < 0 or r < 0:
        raise Exception('combinations_with_replacement_count(n, r) not defined when n or r is negative')
    elif n == 0:
        return 1
    else:
        return combinations_count(n + r - 1, r)
from collections import deque, Counter, defaultdict 
from heapq import heapify, heappop, heappush, heappushpop, heapreplace,nlargest,nsmallest 
from copy import deepcopy, copy 
from operator import itemgetter


from functools import reduce
from fractions import gcd 
def gcds(numbers):
    return reduce(gcd, numbers)
def lcm(x, y):
    return (x * y) // gcd(x, y)
def lcms(numbers):
    return reduce(lcm, numbers, 1)



INF = 10 ** 18
MOD = 10 ** 9 + 7
modpow = lambda a, n, p = MOD: pow(a, n, p) 
def modinv(a, p = MOD):
    
    
    return modpow(a, p-2, p)
def modinv_list(n, p = MOD):
    if n <= 1:
        return [0,1][:n+1]
    else:
        inv_t = [0,1]
        for i in range(2, n+1):
            inv_t += [inv_t[p % i] * (p - int(p / i)) % p]
        return inv_t
def modfactorial_list(n, p = MOD):
    if n == 0:
        return [1]
    else:
        l = [0] * (n+1)
        tmp = 1
        for i in range(1, n+1):
            tmp = tmp * i % p
            l[i] = tmp
        return l
def modcomb(n, k, fac_list = [], p = MOD):
    
    
    from math import factorial
    if n < 0 or k < 0 or n < k: return 0
    if n == 0 or k == 0: return 1
    if len(fac_list) <= n:
        a = factorial(n) % p
        b = factorial(k) % p
        c = factorial(n-k) % p
    else:
        a = fac_list[n]
        b = fac_list[k]
        c = fac_list[n-k]
    return (a * modpow(b, p-2, p) * modpow(c, p-2, p)) % p
def modadd(a, b, p = MOD):
    return (a + b) % MOD
def modsub(a, b, p = MOD):
    return (a - b) % p
def modmul(a, b, p = MOD):
    return ((a % p) * (b % p)) % p
def moddiv(a, b, p = MOD):
    return modmul(a, modpow(b, p-2, p))



    
    

    
    
    
    
    

    
    

    
    


r = lambda: sys.stdin.readline().strip()
r_int = lambda: int(r())
R = lambda: list(map(int, r().split()))
Rfloat = lambda: list(map(float, r().split()))
Rtuple = lambda: tuple(map(int, r().split()))
Rmap = lambda: map(int, r().split())










N = r_int()
Dic = defaultdict(list)
V = 10**5 + 5
Arrived = [False] * (2 * V)
for _ in range(N):
    x, y = R()
    y += V
    Dic[x].append(y)
    Dic[y].append(x)

ans = 0
task = []
for k, v in Dic.items():
    if Arrived[k]:
        continue
    Arrived[k] = True
    cntx = 0
    cnty = 0
    if k < V:
        cntx += 1
    else:
        cnty += 1
    cntdot = 0
    task.extend(v)
    cntdot += len(v)
    while task:
        s = task.pop()
        if Arrived[s]:
            continue
        if s < V:
            cntx += 1
        else:
            cnty += 1
        Arrived[s] = True
        task.extend(Dic[s])
        cntdot += len(Dic[s])
    ans += cntx * cnty - cntdot // 2
print(ans)

Solution (Python):
N = int(input())
points = []
for i in range(0, N):
    points.append(list(map(int, input().split(' '))))
X = dict()
Y = dict()
count = 0
for p in points:
    if p[0] not in X.keys():
        X[p[0]] = []
    X[p[0]].append(p[1])
    if p[1] not in Y.keys():
        Y[p[1]] = []
    Y[p[1]].append(p[0])
while len(X.items()) > 0:
    item = list(X.items())[0]
    del X[item[0]]
    queue = [item]
    point_dict = {'x': [], 'y': []}
    queue_is_x = [True]
    while len(queue) > 0:
        item = queue.pop(0)
        is_x = queue_is_x.pop(0)
        if is_x:
            point_dict['x'].append(item[0])
            for i in item[1]:
                if i in Y.keys():
                    queue.append((i, Y[i]))
                    queue_is_x.append(False)
                    del Y[i]
        else:
            point_dict['y'].append(item[0])
            for i in item[1]:
                if i in X.keys():
                    queue.append((i, X[i]))
                    queue_is_x.append(True)
                    del X[i]
    
    
    
    
    count += len(set(point_dict['x'])) * len(set(point_dict['y']))

print(count - len(points))

Solution (Python):
N = int(input())
xy = [list(map(int, input().split())) for _ in range(N)]

class Union:
  def __init__(s, n):
    s.N = n
    s.L = [-1] * n

  def root(s, x):
    if s.L[x] < 0:
      return x, -s.L[x]
    return s.root(s.L[x])

  def merge(s, x, y):
    a, ar = s.root(x)
    b, br = s.root(y)
    if a == b:
      return True
    if ar < br:
      s.L[b] -= ar
      s.L[a] = b
    else:
      s.L[a] -= br
      s.L[b] = a
    return False

  def getList(s):
    D = []
    DN = [0] * N
    for i in range(N):
      if s.L[i] < 0:
        DN[i] = len(D)
        D.append([])
    for i in range(N):
      D[DN[s.root(i)[0]]].append(i)
    return D

M = 10**5 + 1
xL = [-1] * M
yL = [-1] * M
uni = Union(N)

for i in range(N):
  x = xy[i][0]
  y = xy[i][1]
  if xL[x] != -1:
    uni.merge(xL[x], i)
  if yL[y] != -1:
    uni.merge(yL[y], i)
  xL[x] = i
  yL[y] = i

L = uni.getList()
ans = 0
for t in L:
  xL = [0] * M
  yL = [0] * M
  xN = 0
  yN = 0
  for i in t:
    x, y = xy[i]
    if xL[x] == 0:
      xL[x] = 1
      xN += 1
    if yL[y] == 0:
      yL[y] = 1
      yN += 1
  ans += xN * yN - len(t)

print(ans)

Solution (Python):
from collections import defaultdict,deque
import sys,heapq,bisect,math,itertools,string,queue
sys.setrecursionlimit(10**8)
INF = float('inf')
mod = 10**9+7
eps = 10**-7
def inpl(): return list(map(int, input().split()))
def inpl_str(): return list(input().split())


class PotentialUnionFind:
    def __init__(self,N): 
        self.table = [i for i in range(N)]    
        self.rank  = [1 for i in range(N)]    
        self.size  = [1 for i in range(N)]    
        self.diffweight = [0 for i in range(N)]

    def Find(self,x):    
        if self.table[x] == x:
            return x
        else:
            root = self.Find(self.table[x]) 
            self.size[x] = self.size[self.table[x]]
            self.diffweight[x] += self.diffweight[self.table[x]]
            self.table[x] = root
            return root

    def Unite(self,x,y,w): 
        w = w - self.Weight(y) + self.Weight(x)
        x,y = self.Find(x), self.Find(y)
        sx,sy = self.Size(x), self.Size(y)
        if x == y: return
        if self.rank[x] > self.rank[y]:
            self.table[y] = x
            self.size[x] = sx + sy
            self.diffweight[y] = w
        else:
            self.table[x] = y
            self.size[y] = sx + sy
            self.diffweight[x] = -w
            if self.rank[x] == self.rank[y]:
                self.rank[y] += 1

    def Check(self,x,y):
        return self.Find(x) == self.Find(y)

    def Size(self,x):
        return self.size[self.Find(x)]

    def Weight(self,x): 
        self.Find(x)
        return self.diffweight[x]

    def Diff(self,x,y): 
        return self.Weight(y) - self.Weight(x)


N = int(input())
PUF = PotentialUnionFind(10**5+1)

xys = [inpl() for _ in range(N)]
xys.sort()
tatedict = defaultdict(list)
xx = set()
for x,y in xys:
    tatedict[x].append(y)
    xx.add(x)

for x in xx:
    s = tatedict[x][0]
    for t in tatedict[x]:
        if not PUF.Check(s,t):
            PUF.Unite(s,t,1)

ans = 0
for x in xx:
    s = tatedict[x][0]
    ans += PUF.Size(s) - len(tatedict[x])

print(ans)

Solution (Python):
from collections import defaultdict,deque
import sys,heapq,bisect,math,itertools,string,queue
sys.setrecursionlimit(10**8)
INF = float('inf')
mod = 10**9+7
eps = 10**-7
def inpl(): return list(map(int, input().split()))
def inpl_str(): return list(input().split())

class UnionFind:
    def __init__(self,N): 
        self.table = [i for i in range(N)]    
        self.rank  = [1 for i in range(N)]    
        self.size  = [1 for i in range(N)]    

    def Find(self,x):    
        if self.table[x] == x:
            return x
        else:
            self.table[x] = self.Find(self.table[x])    
            self.size[x] = self.size[self.table[x]]
            return self.table[x]

    def Unite(self,x,y):    
        x,y = self.Find(x), self.Find(y)
        sx,sy = self.Size(x), self.Size(y)
        if x == y: return
        if self.rank[x] > self.rank[y]:
            self.table[y] = x
            self.size[x] = sx + sy
        else:
            self.table[x] = y
            self.size[y] = sx + sy
            if self.rank[x] == self.rank[y]:
                self.rank[y] += 1

    def Check(self,x,y):
        return self.Find(x) == self.Find(y)

    def Size(self,x):
        return self.size[self.Find(x)]



N = int(input())
UF = UnionFind(10**5+1)

xys = [inpl() for _ in range(N)]
xys.sort()
tatedict = defaultdict(list)
xx = set()
for x,y in xys:
    tatedict[x].append(y)
    xx.add(x)

for x in xx:
    s = tatedict[x][0]
    for t in tatedict[x]:
        if not UF.Check(s,t):
            UF.Unite(s,t)

ans = 0
for x in xx:
    s = tatedict[x][0]
    ans += UF.Size(s) - len(tatedict[x])

print(ans)

Solution (Python):
V=10**5+5
class UnionFind:
    def __init__(self,n):
        self.parent=[i for i in range(n)]
        self.rank=[0 for _ in range(n)]
        
        self.xSize=[1 if i<V else 0 for i in range(n)]
        self.ySize=[0 if i<V else 1 for i in range(n)]

    def find(self,x):
        if self.parent[x]==x:
            return x
        else:
            parentx=self.find(self.parent[x])
            self.parent[x]=parentx
            return parentx

    def union(self,x,y):
        rootx=self.find(x)
        rooty=self.find(y)
        if rootx==rooty:
            return
        if self.rank[rootx]>self.rank[rooty]:
            self.parent[rooty]=rootx
            self.xSize[rootx]+=self.xSize[rooty]
            self.ySize[rootx]+=self.ySize[rooty]
        else:
            self.parent[rootx]=rooty
            self.xSize[rooty]+=self.xSize[rootx]
            self.ySize[rooty]+=self.ySize[rootx]
            if self.rank[rootx]==self.rank[rooty]:
                self.rank[rooty]+=1

n=int(input())
xy=[tuple(map(int,input().split())) for _ in range(n)]
uf=UnionFind(V*2)
for xi,yi in xy:
    uf.union(xi,yi+V)




ans=0
used=set()
for i in range(V*2):
    rooti=uf.find(i)
    if rooti not in used:
        used.add(rooti)
        ans+= uf.xSize[rooti]*uf.ySize[rooti]
print(ans-n)