Problem:
Score : 400 points

Problem Statement

There are N persons called Person 1 through Person N.

You are given M facts that "Person A_i and Person B_i are friends." The same fact may be given multiple times.

If X and Y are friends, and Y and Z are friends, then X and Z are also friends. There is no friendship that cannot be derived from the M given facts.

Takahashi the evil wants to divide the N persons into some number of groups so that every person has no friend in his/her group.

At least how many groups does he need to make?

Constraints

2 \leq N \leq 2\times 10^5

0 \leq M \leq 2\times 10^5

1\leq A_i,B_i\leq N

A_i \neq B_i

Input

Input is given from Standard Input in the following format:

N M
A_1 B_1
\vdots
A_M B_M

Output

Print the answer.

Sample Input 1

5 3
1 2
3 4
5 1

Sample Output 1

3

Dividing them into three groups such as \{1,3\}, \{2,4\}, and \{5\} achieves the goal.

Sample Input 2

4 10
1 2
2 1
1 2
2 1
1 2
1 3
1 4
2 3
2 4
3 4

Sample Output 2

4

Sample Input 3

10 4
3 1
4 1
5 9
2 6

Sample Output 3

3

Solution in C# (.NET Core 3.1.201) (judge verdict: Accepted)

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Xml.Resolvers;

namespace CompetitionPrograming2
{
    public sealed class Program : BaseProgram
    {
        public static void Main(string[] _) { using var c = new SetConsole(); Solve(); }
        public static void Solve()
        {
            var (n, m) = GetArray<int>();
            var uf = new UnionFind(n);

            for (int i = 0; i < m; i++)
            {
                var (a, b) = GetArray<int>();
                uf.Unite(a - 1, b - 1);
            }

            var groupIds = new HashSet<int>();
            for (int i = 0; i < n; i++)
            {
                groupIds.Add(uf.Find(i));
            }
            Write(groupIds.Max(uf.Size));
        }

        public class UnionFind
        {
            // 子は親のIDを正で持つ 根は木のサイズを負で持つ
            private readonly int[] d;
            public UnionFind(int n = 0)
            {
                // 初めはすべてが根
                d = Enumerable.Repeat(-1, n).ToArray();
            }

            /// <summary>
            /// 集合のIDを返す
            /// </summary>
            /// <param name="x">ノード</param>
            /// <returns>集合のID</returns>
            public int Find(int x)
            {
                // xが親だった場合
                if (d[x] < 0) { return x; }

                return d[x] = Find(d[x]);
            }

            /// <summary>
            /// 両引数が属する集合同士を連結する
            /// </summary>
            /// <param name="x">ノード1</param>
            /// <param name="y">ノード1</param>
            /// <returns>連結されたかどうか</returns>
            public bool Unite(int x, int y)
            {
                x = Find(x); y = Find(y);

                if (x == y) { return false; }

                // yをxへ連結するが、サイズが小さい方の木を大きい方の木へ連結すべきなので
                // size(y) > size(x)ならばswapする
                // サイズを表すd[x] d[y]は両方とも負なので、不等号の向きに注意
                if (d[x] > d[y]) { Swap(ref x, ref y); }

                d[x] += d[y];
                d[y] = x;
                return true;
            }

            /// <summary>
            /// 両引数が属する集合が同じかどうか
            /// </summary>
            /// <param name="x">ノード1</param>
            /// <param name="y">ノード2</param>
            /// <returns></returns>
            public bool Same(int x, int y) => Find(x) == Find(y);

            /// <summary>
            /// 集合の大きさを返す
            /// </summary>
            /// <param name="x">ノード</param>
            /// <returns>集合の大きさ</returns>
            public int Size(int x) => -d[Find(x)];

            private static void Swap<T>(ref T item1, ref T item2)
            {
                var tmp = item1;
                item1 = item2;
                item2 = tmp;
            }
        }

    }
}
public abstract class BaseProgram
{
    protected readonly static long divisor = 1000000007;
    protected static Action<object> Write => Console.WriteLine;
    protected static string GetString()
    {
        var str = Console.ReadLine();
        if (str is null) { throw new NullReferenceException("標準入力がnullです Solveメソッド内の解答コードが間違っています"); }
        return str;
    }
    protected static T GetNumber<T>() where T : IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
    {
        var str = GetString();
        return Activator.CreateInstance<T>() switch
        {
            int _ => (T)(object)str.ToInt(),
            long _ => (T)(object)str.ToLong(),
            double _ => (T)(object)str.ToDouble(),
            decimal _ => (T)(object)str.ToDecimal(),
            BigInteger _ => (T)(object)str.ToBigInteger(),
            _ => throw new NotSupportedException(),
        };
    }
    protected static T[] GetArray<T>() where T : IComparable, IComparable<T>, IEquatable<T> => ToArray<T>(GetString());
    protected static List<T> GetList<T>() where T : IComparable, IComparable<T>, IEquatable<T> => ToList<T>(GetString());
    protected static void Swap<T>(ref T item1, ref T item2) => (item2, item1) = (item1, item2);
    protected static IEnumerable<char> AtoZ()
    {
        for (char i = 'a'; i <= 'z'; i++)
        {
            yield return i;
        }
    }
    private static T[] ToArray<T>(string str) where T : IComparable, IComparable<T>, IEquatable<T>
    {
        return ConvertEnumerator<T>(str).ToArray();
    }
    private static List<T> ToList<T>(string str) where T : IComparable, IComparable<T>, IEquatable<T>
    {
        return ConvertEnumerator<T>(str).ToList();
    }
    private static IEnumerable<T> ConvertEnumerator<T>(string str) where T : IComparable, IComparable<T>, IEquatable<T>
    {
        return Activator.CreateInstance<T>() switch
        {
            byte _ => str.Split().Select(byte.Parse).Cast<T>(),
            int _ => str.Split().Select(int.Parse).Cast<T>(),
            long _ => str.Split().Select(long.Parse).Cast<T>(),
            double _ => str.Split().Select(double.Parse).Cast<T>(),
            decimal _ => str.Split().Select(decimal.Parse).Cast<T>(),
            BigInteger _ => str.Split().Select(BigInteger.Parse).Cast<T>(),
            _ => throw new NotSupportedException()
        };
    }
    protected sealed class SetConsole : IDisposable
    {
        readonly StreamWriter sw = new StreamWriter(Console.OpenStandardOutput());
        public SetConsole()
        {
            sw.AutoFlush = false;
            Console.SetOut(sw);
        }
        public void Dispose()
        {
            Console.Out.Flush();
            sw.AutoFlush = true;
            Console.SetOut(sw);
        }
    }
}
public static class ExtentionsLibrary
{
    public static long Choose(this long n, long a, long divisor = 1000000007)
    {
        long numerator = 1, denominator = 1;
        for (int i = 0; i < a; i++)
        {
            numerator *= n - i;
            numerator %= divisor;
            denominator *= i + 1;
            denominator %= divisor;
        }
        return numerator / denominator;
    }
    public static long Choose(this int n, int a, int divisor = 1000000007)
    {
        long numerator = 1, denominator = 1;
        for (int i = 0; i < a; i++)
        {
            numerator *= n - i;
            numerator %= divisor;
            denominator *= i + 1;
            denominator %= divisor;
        }
        return numerator / denominator;
    }
    /// <summary>
    /// 階乗を求める
    /// </summary>
    /// <param name="num"></param>
    /// <returns></returns>
    public static long Factorial(this long num)
    {
        var result = 1L;
        for (int i = 1; i <= num; i++)
        {
            result *= i;
        }
        return result;
    }
    private static T[] NextPermutation<T>(this IEnumerable<T> collection, IComparer<T> cmp) where T : IComparable
    {
        var array = collection.ToArray();
        int? exchangingNumIndex = null;
        for (int i = 1; i < array.Length; i++)
        {
            if (cmp.Compare(array[i - 1], array[i]) >= 0) { continue; }

            exchangingNumIndex = i - 1;
        }

        if (exchangingNumIndex is null) { return null; }

        for (int j = array.Length - 1; j >= 0; j--)
        {
            if (cmp.Compare(array[(int)exchangingNumIndex], array[j]) >= 0) { continue; }

            var tmp = array[j];
            array[j] = array[(int)exchangingNumIndex];
            array[(int)exchangingNumIndex] = tmp;
            array = array.Take((int)exchangingNumIndex + 1).Concat(array.Skip((int)exchangingNumIndex + 1).Reverse()).ToArray();
            break;
        }
        return array;
    }
    public static string[] NextPermutation(this IEnumerable<string> collection)
    {
        return NextPermutation(collection, StringComparer.Ordinal);
    }
    public static T[] NextPermutation<T>(this IEnumerable<T> collection) where T : IComparable
    {
        return NextPermutation(collection, Comparer<T>.Default);
    }
    public static IEnumerable<T[]> Permutations<T>(this IEnumerable<T> collection) where T : IComparable
    {
        var next = collection.NextPermutation();

        while (next != null)
        {
            yield return next;

            next = next.NextPermutation();
        }
    }
    public static long Gcd(this long a, long b)
    {
        if (a < b) { return Gcd(b, a); }
        long tmp;
        do
        {
            tmp = a % b;
            a = b;
            b = tmp;
        } while (tmp != 0);
        return a;
    }
    public static long Lcm(this long a, long b) => a * b / Gcd(a, b);
    /// <summary>
    /// 素因数分解を行う
    /// </summary>
    /// <param name="num"></param>
    /// <returns></returns>
    public static IEnumerable<(long num, int count)> Factorize(this long n)
    {
        yield return (1, 1);
        var rootN = Math.Sqrt(n);
        for (long i = 2; i <= rootN; i++)
        {
            if (n % i != 0) { continue; }

            var count = 1;
            n /= i;
            while (n % i == 0)
            {
                n /= i;
                count++;
            }
            yield return (i, count);
        }
        if (n != 1) { yield return (n, 1); }
    }
    public static int ToInt(this string str) => int.Parse(str);
    public static int ToInt(this char chr) => int.Parse(chr.ToString());
    public static long ToLong(this string str) => long.Parse(str);
    public static double ToDouble(this string str) => double.Parse(str);
    public static decimal ToDecimal(this string str) => decimal.Parse(str);
    public static BigInteger ToBigInteger(this string str) => BigInteger.Parse(str);
    public static DateTime ToDateTime(this string str) => DateTime.Parse(str);
    public static string StringJoin<T>(this IEnumerable<T> collection, string separator = "") => string.Join(separator, collection.Select(c => c.ToString()));
    public static string StringConcat(this IEnumerable<char> collection) => string.Concat(collection);
    public static int LowerBound<T>(this IReadOnlyList<T> a, T v) => LowerBound(a, v, Comparer<T>.Default);
    public static int LowerBound(this IReadOnlyList<string> str, string v) => LowerBound(str, v, StringComparer.Ordinal);
    public static int LowerBound<T>(this IReadOnlyList<T> a, T v, IComparer<T> cmp)
    {
        var l = 0;
        var r = a.Count - 1;
        while (l <= r)
        {
            var mid = l + (r - l) / 2;
            var result = cmp.Compare(a[mid], v);
            if (result == -1)
            {
                l = mid + 1;
            }
            else
            {
                r = mid - 1;
            }
        }
        return l;
    }
    public static int UpperBound(this IReadOnlyList<string> str, string v) => UpperBound(str, v, StringComparer.Ordinal);
    public static int UpperBound<T>(this IReadOnlyList<T> a, T v) => UpperBound(a, v, Comparer<T>.Default);
    public static int UpperBound<T>(this IReadOnlyList<T> a, T v, IComparer<T> cmp)
    {
        var l = 0;
        var r = a.Count - 1;
        while (l <= r)
        {
            var mid = l + (r - l) / 2;
            var res = cmp.Compare(a[mid], v);
            if (res <= 0) l = mid + 1;
            else r = mid - 1;
        }
        return l;
    }
    public static IOrderedEnumerable<TSource> OrderBy<TSource>(this IEnumerable<TSource> source, Func<TSource, string> keySelector)
    {
        return source.OrderBy(keySelector, StringComparer.Ordinal);
    }
    public static IOrderedEnumerable<TSource> OrderByDescending<TSource>(this IEnumerable<TSource> source, Func<TSource, string> keySelector)
    {
        return source.OrderByDescending(keySelector, StringComparer.Ordinal);
    }
    public static void Deconstruct<T>
    (
        this IList<T> self,
        out T first
    )
    {
        first = self[0];
    }

    public static void Deconstruct<T>
    (
        this IList<T> self,
        out T first,
        out T second
    )
    {
        first = self[0];
        second = self[1];
    }

    public static void Deconstruct<T>
    (
        this IList<T> self,
        out T first,
        out T second,
        out T third
    )
    {
        first = self[0];
        second = self[1];
        third = self[2];
    }

    public static void Deconstruct<T>
    (
        this IList<T> self,
        out T first,
        out T second,
        out T third,
        out T forth
    )
    {
        first = self[0];
        second = self[1];
        third = self[2];
        forth = self[3];
    }
    public static void Deconstruct<T>
    (
        this IList<T> self,
        out T first,
        out T second,
        out T third,
        out T forth,
        out T fifth
    )
    {
        first = self[0];
        second = self[1];
        third = self[2];
        forth = self[3];
        fifth = self[4];
    }
}

public struct Mint
{
    private static readonly long MOD = (long)1e9 + 7;

    /// <summary>
    /// 0以上MOD未満の整数
    /// </summary>
    public long Value;

    public Mint(long val)
    {
        Value = val % MOD;
        if (Value < 0) Value += MOD;
    }

    private static Mint Ctor(long val) => new Mint() { Value = val };
    public static Mint operator +(Mint a, Mint b)
    {
        long res = a.Value + b.Value;
        if (res > MOD) res -= MOD;
        return Ctor(res);
    }
    public static Mint operator -(Mint a, Mint b)
    {
        long res = a.Value - b.Value;
        if (res < 0) res += MOD;
        return Ctor(res);
    }
    public static Mint operator *(Mint a, Mint b)
    {
        long res = a.Value * b.Value;
        if (res > MOD) res %= MOD;
        return Ctor(res);
    }
    public static Mint operator /(Mint a, Mint b) => a * Inv(b);
    public static Mint Pow(Mint a, long n)
    {
        if (n == 0) return new Mint(1);
        Mint b = Pow(a, n >> 1);
        b *= b;
        if ((n & 1) == 1) b *= a;
        return b;
    }
    public static Mint Inv(Mint n)
    {
        long a = n.Value;
        long b = MOD;

        long x = 1;
        long u = 0;
        while (b != 0)
        {
            long k = a / b;
            long _x = u;
            u = x - k * u;
            x = _x;

            long _a = a;
            a = b;
            b = _a - (k * b);
        }

        return new Mint(x);
    }
    public override string ToString() => Value.ToString();
    public static implicit operator Mint(long a) => new Mint(a);
    public static explicit operator long(Mint a) => a.Value;
    public static Mint Choose(long n, long k)
    {
        Mint numerator = 1, denominator = 1;
        for (int i = 0; i < k; i++)
        {
            numerator *= n - i;
            denominator *= i + 1;
        }
        return numerator / denominator;
    }
}
public enum Priority : byte
{
    Smaller = 0,
    Larger = 1
}