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 in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

namespace A
{
    partial class Program
    {
        public class Cdn
        {
            public int Idx { get; set; }
            public long X { get; set; }
            public long Y { get; set; }
        }

        long n;
        List<Cdn> cs = new List<Cdn>();
        HashSet<long> xCheckeds = new HashSet<long>();
        HashSet<long> yCheckeds = new HashSet<long>();
        Dictionary<long, List<Cdn>> xGroups;
        Dictionary<long, List<Cdn>> yGroups;
        public IEnumerable<string> Solve()
        {
            n = Num;
            for (int i = 0; i < n; i++)
            {
                cs.Add(new Cdn() { Idx = i, X = Num, Y = Num });
            }

            xGroups = cs.GroupBy(x => x.X).ToDictionary(x => x.Key, x => x.ToList());
            yGroups = cs.GroupBy(x => x.Y).ToDictionary(x => x.Key, x => x.ToList());

            long total = 0;

            for (int i = 0; i < n; i++)
            {
                var x = cs[i].X;
                var y = cs[i].Y;

                if (xCheckeds.Contains(x) || yCheckeds.Contains(y))
                    continue;

                var xs = new HashSet<long>();
                var ys = new HashSet<long>();
                Check(i, ref xs, ref ys);
                total += (long)xs.Count * (long)ys.Count;
            }

            var res = total - n;
            yield return res.ToString();
        }

        void Check(int i, ref HashSet<long> xs, ref HashSet<long> ys)
        {
            HashSet<long> axs = new HashSet<long>();
            HashSet<long> ays = new HashSet<long>();
            HashSet<long> bxs = new HashSet<long>();
            HashSet<long> bys = new HashSet<long>();
            axs.Add(cs[i].X);
            ays.Add(cs[i].Y);
            xs.Add(cs[i].X);
            ys.Add(cs[i].Y);

            while (axs.Count > 0 || ays.Count > 0)
            {
                bxs.Clear();
                bys.Clear();

                var nys = axs.SelectMany(x => xGroups[x].Select(xt => xt.Y)).Where(x => !yCheckeds.Contains(x));
                var nxs = ays.SelectMany(x => yGroups[x].Select(xt => xt.X)).Where(x => !xCheckeds.Contains(x));

                foreach (var y in nys)
                {
                    bys.Add(y);
                    yCheckeds.Add(y);
                    if (!ys.Contains(y))
                        ys.Add(y);
                }

                foreach (var x in nxs)
                {
                    bxs.Add(x);
                    xCheckeds.Add(x);
                    if (!xs.Contains(x))
                        xs.Add(x);
                }

                var temp = axs;
                axs = bxs;
                bxs = temp;
                temp = ays;
                ays = bys;
                bys = temp;
            }
        }
    }

    class StartingPoint
    {
        static void Main(string[] args)
        {
            if (args?.Any() == true)
            {
                var assembly = Assembly.GetExecutingAssembly();
                var resourceName = "AtCoder.Resources.In.txt";
                var stream = assembly.GetManifestResourceStream(resourceName);
                var streamReader = new StreamReader(stream, Encoding.UTF8);
                var str = streamReader.ReadToEnd();
                var textReader = new StringReader(str);
                Console.SetIn(textReader);
            }

            try
            {
                foreach (var s in new Program().Solve())
                {
                    Console.WriteLine(s);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                if (args?.Any() != true)
                    throw e;
            }

            if (args?.Any() == true)
                Console.ReadLine();
        }
    }

    partial class Program
    {
        long[] Nums(long count) => Enumerable.Range(0, (int)count).Select(_ => Num).ToArray();
        long Num => long.Parse(Str);
        double NumD => (double)Num;

        StringBuilder sb = new StringBuilder();

        string Str => GetStr();
        string GetStr()
        {
            sb.Clear();

            int r = 0;
            bool? b = null;
            do
            {
                r = Console.Read();
                b = IsBreak(r);
            } while (b == true);

            if (b == null)
                throw new Exception("input error: unexpected end of stream");

            do
            {
                sb.Append((char)r);
                r = Console.Read();
                b = IsBreak(r);
            } while (b == false);

            return sb.ToString();
        }

        bool? IsBreak(int c)
        {
            switch (c)
            {
                case -1:
                    return null;
                case (int)' ':
                case (int)'\n':
                case (int)'\r':
                    return true;
                default:
                    return false;
            }
        }
    }
}