#include "bits/stdc++.h"
using namespace std;
using ll = long long int;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pll = pair<ll, ll>;
// Graph.h start--------------------------------------------
#pragma once

#include <vector>

using Weight = long long;

struct Vertex;
using Vertices = std::vector<Vertex *>;
struct Edge;
using Edges = std::vector<const Edge *>;

struct Vertex {
	const int id;
	Edges edges;
	Vertex(int id);
	void add_edge(const Edge *const p_e);
	void add_edge(const Edges &es);
};

struct Edge {
	Vertex *p_src, *p_dst;
	Weight weight;

	Edge(Vertex *p_src, Vertex *p_dst, Weight weight);
	Edge(const Edge &e);
	Edge& operator = (const Edge &de);
};

struct Graph {
	Vertices vertices;
	Edges edges;
	Graph(int nvertices);
	Graph();
	~Graph();
	Graph(Graph&& r)
	{
		vertices = move(r.vertices);
		edges = move(r.edges);
	}
	Graph& operator=(Graph&& r)
	{
		for (auto &p_vertex : vertices) {
			delete p_vertex;
		}
		for (auto &p_edge : edges) {
			delete p_edge;
		}
		vertices = move(r.vertices);
		edges = move(r.edges);
		return *this;
	}
	void add_edge(Edge *p_edge);
	void add_edge(int edge_src, int edge_dst, Weight w);
	void add_edge(int edge_src, int edge_dst);
};

//----------------------------------------------------------

using namespace std;

Vertex::Vertex(int id) : id(id) { }
void Vertex::add_edge(const Edge *const p_e) {
	edges.push_back(p_e);
}
void Vertex::add_edge(const Edges &es) {
	for (const auto &p_e : es) {
		add_edge(p_e);
	}
}

Edge::Edge(Vertex *p_src, Vertex *p_dst, Weight weight) : p_src(p_src), p_dst(p_dst), weight(weight) { }
Edge::Edge(const Edge &e) : Edge(e.p_src, e.p_dst, e.weight) {}
Edge& Edge::operator = (const Edge &e) {
	p_src = e.p_src;
	p_dst = e.p_dst;
	weight = e.weight;
	return *this;
}
bool operator < (const Edge &e1, const Edge &e2) {
	return e1.weight != e2.weight ? e1.weight > e2.weight : // !!INVERSE!!
	e1.p_src != e2.p_src ? e1.p_src->id < e2.p_src->id : e1.p_dst->id < e2.p_dst->id;
}

Graph::Graph(int nvertices) {
	for (int id = 0; id < nvertices; id++) {
		vertices.push_back(new Vertex(id));
	}
}
Graph::Graph() : Graph(0) {}
Graph::~Graph() {
	for (auto &p_vertex : vertices) {
		delete p_vertex;
	}
	for (auto &p_edge : edges) {
		delete p_edge;
	}
}
void Graph::add_edge(Edge *p_edge) {
	vertices[p_edge->p_src->id]->add_edge(p_edge);
	edges.push_back(p_edge);
}
void Graph::add_edge(int edge_src, int edge_dst, Weight w) {
	add_edge(new Edge(vertices[edge_src], vertices[edge_dst], w));
}
void Graph::add_edge(int edge_src, int edge_dst) {
	add_edge(edge_src, edge_dst, 0);
}

// Graph.h end----------------------------------------------

int N;
vector<int> x, y;
vector<bool> memo;
Graph g;
#define MAXX 100000

pii dfs(int i) {
	if (memo[i])
		return pii(0, 0);
	memo[i] = true;

	pii res(0, 0);
	if (i < MAXX)
		res.first++;
	else
		res.second++;
	for (const auto& pe : g.vertices[i]->edges)
	{
		pii tp = dfs(pe->p_dst->id);
		res.first += tp.first;
		res.second += tp.second;
	}
	return res;
}


int main() {
	cin >> N;
	x.assign(N, 0);
	y.assign(N, 0);
	for (int i = 0; i < N; i++) {
		cin >> x[i] >> y[i];
		x[i]--;
		y[i]--;
	}

	g = Graph(2 * MAXX);
	for (int i = 0; i < N; i++)
	{
		g.add_edge(x[i], MAXX + y[i]);
		g.add_edge(MAXX + y[i], x[i]);
	}
	memo.assign(2 * MAXX, false);

	ll ans = -N;
	for (int i = 0; i < N; i++)
	{
		pii nxny;
		nxny = dfs(x[i]);
		ans += (ll)nxny.first * nxny.second;
		nxny = dfs(y[i]);
		ans += (ll)nxny.first * nxny.second;
	}
	cout << ans << endl;
	return 0;
}


Measured memory_bytes: 27504