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++ (GCC 9.2.1) (judge verdict: Accepted)

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>

using namespace std;
using ll = long long;

// struct UnionFind {
// vector<int> par;
// UnionFind(int N) : par(N) {
// for (int i = 0; i < N; i++) par[i] = i;
// }

// int root(int x) {
// if (par[x] == x) return x;
// return par[x] = root(par[x]);
// }

// void unite(int x, int y) {
// int rx = root(x);
// int ry = root(y);
// if (rx == ry) return;
// par[rx] = ry;
// }

// bool same(int x, int y) {
// int rx = root(x);
// int ry = root(y);
// return rx == ry;
// }
// };

// union by size + path having
class UnionFind {
   public:
    vector<ll> par;  // 各元の親を表す配列
    vector<ll> siz;  // 素集合のサイズを表す配列(1 で初期化)

    // Constructor
    UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
        for (ll i = 0; i < sz_; ++i) par[i] = i;  // 初期では親は自分自身
    }
    void init(ll sz_) {
        par.resize(sz_);
        siz.assign(sz_, 1LL);  // resize だとなぜか初期化されなかった
        for (ll i = 0; i < sz_; ++i) par[i] = i;  // 初期では親は自分自身
    }

    // Member Function
    // Find
    ll root(ll x) {  // 根の検索
        while (par[x] != x) {
            x = par[x] = par[par[x]];  // x の親の親を x の親とする
        }
        return x;
    }

    // Union(Unite, Merge)
    bool merge(ll x, ll y) {
        x = root(x);
        y = root(y);
        if (x == y) return false;
        // merge technique（データ構造をマージするテク．小を大にくっつける）
        if (siz[x] < siz[y]) swap(x, y);
        siz[x] += siz[y];
        par[y] = x;
        return true;
    }

    bool issame(ll x, ll y) {  // 連結判定
        return root(x) == root(y);
    }

    ll size(ll x) {  // 素集合のサイズ
        return siz[root(x)];
    }
};

int main() {
    int N, M;
    cin >> N >> M;

    UnionFind tree(N + 1);
    tree.merge(0, 0);
    for (int i = 0; i < M; i++) {
        int a, b;
        cin >> a >> b;
        tree.merge(a, b);
    }

    int output = 0;
    for (int i = 0; i < N; i++) {
        if (tree.size(i + 1) > output) {
            output = tree.size(i + 1);
        }
        // cout << tree.size(i + 1) << endl;
    }
    cout << output << endl;

    return 0;
}