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 Rust (1.42.0) (judge verdict: Accepted)

#![allow(unused_imports)]

// language: Rust(1.42.0)
// check available crates on AtCoder at "https://atcoder.jp/contests/language-test-202001"
// My Library Repositry is at "https://github.com/Loptall/sfcpl"

/*
    水コーダーになれてうれしいです
*/

// from std...
use std::cmp::{
    max, min, Ordering,
    Ordering::{Equal, Greater, Less},
    Reverse,
};
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
use std::convert::TryInto;
use std::fmt;
use std::mem::swap;
use std::num::{NonZeroU32, ParseIntError};
use std::ops::{
    Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
    Index, IndexMut, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub,
    SubAssign,
};
use std::process::exit;
use std::{f32, f64, i128, i16, i32, i64, i8, isize, u128, u16, u32, u64, u8, usize};

// Dep' crates are...
use itertools::*;
use itertools_num::*;
use lazy_static::lazy_static;
use maplit::{btreemap, btreeset, hashmap, hashset};
use num_bigint::{BigInt, BigUint};
use num_complex::Complex;
use num_integer::{binomial, gcd, lcm, multinomial, Integer};
use num_rational::Rational;
use num_traits::{
    clamp, one, pow, zero, Num, NumAssignOps, NumOps, One, Pow, Signed, Unsigned, Zero,
};
use permutohedron::Heap;
use proconio::{
    derive_readable, fastout, input, is_stdin_empty,
    marker::{Bytes, Chars, Isize1, Usize1},
};
use rand::random;

pub use visualize::*;
pub mod visualize {
    use itertools::*;
    pub trait Visualize {
        fn visualize(&self, split: &str);
        fn continuous(&self) {
            self.visualize("");
        }
        fn spaces(&self) {
            self.visualize(" ");
        }
        fn lines(&self) {
            self.visualize("\n");
        }
    }
    macro_rules ! impl_vis_for_sized {($ ($ t : ty ) ,+ ) => {$ (impl Visualize for $ t {fn visualize (& self , _split : & str ) {print ! ("{}" , self ) ; } } ) + } ; }
    impl_vis_for_sized! {usize , u8 , u16 , u32 , u64 , u128 , isize , i8 , i16 , i32 , i64 , i128 , String , & str , char }
    impl<T: std::fmt::Display> Visualize for [T] {
        fn visualize(&self, split: &str) {
            print!("{}", self.iter().join(split));
        }
    }
    #[test]
    fn fmt() {
        let a = 2;
        a.visualize("");
        let b = "str";
        b.visualize("");
        let c = vec![1, 2, 3];
        c.visualize(" ");
        c.visualize("\n");
    }
    /// macro to output answer improved to println
    /// this macro will print '\n' at the last.
    /// # Patterns
    /// `,`, it means one space, it formats the elements, which is before, space-split.
    /// And insert a space after the prefix, unless it is at last.
    /// `=>`, it means empty string, it formats the element, witch is before, without any split.
    /// And insert nothing between two element.
    /// `;`, it means newline(\n), it formats the element, witch is before, newline-split.
    /// And insert a newline after the prefix, unless it is at last.
    /// # Eample
    /// ```
    /// #[test]
    /// fn vis_test() {
    ///     let a = 1;
    ///     let b = 2;
    ///     let c = 3;
    ///     let v = vec!['a', 'b', 'c'];
    ///     // single element test ...
    ///     vis!(a); // 1\n
    ///     vis!(a,); // 1\n
    ///     vis!(a =>); // 1\n
    ///     vis!(a;); // 1\n
    ///     println!();
    ///     vis!(a + b); // 3\n
    ///     println!();
    ///     vis!(v); // a b c\n
    ///     vis!(v,); // a b c\n
    ///     vis!(v =>); // abc\n
    ///     vis!(v;); // a\nb\nc\n
    ///     println!();
    ///     // multi elements connected by common operater test ...
    ///     println!();
    ///     vis!(a, b); // 1 2\n
    ///     vis!(a, b, c); // 1 2 3\n
    ///     vis!(a, b, c;); // 1 2 3\n
    ///     println!();
    ///     vis!(a => b); // 12\n
    ///     vis!(a => b => c); // 123\n
    ///     vis!(a => b => c ,); // 123\n
    ///     println!();
    ///     vis!(a; b); // 1\n2\n
    ///     vis!(a; b; c); // 1\n2\n3\n
    ///     vis!(a; b; c =>); // 1\n2\n3\n
    ///     println!();
    ///     // multi elements connected by different operater test ...
    ///     vis!(a, b; c); // 1 2\n3\n
    ///     vis!(a; b, c); // 1\n2 3\n
    ///     vis!(a => b, c); // 12 3\n
    ///     vis!(a; b => c); // 1\n23\n
    ///     println!();
    ///     vis!(a, v); // 1 a b c\n;
    ///     vis!(a, v;); // 1 a\nb\nc\n;
    ///     vis!(a => v =>); // 1abc\n;
    ///     println!("\d");
    ///     // panic!()
    /// }
    /// ```
    #[macro_export]
    macro_rules ! vis {() => {println ! () ; } ; ($ last : expr ; ) => {$ last . lines () ; vis ! () } ; ($ last : expr => ) => {$ last . continuous () ; vis ! () ; } ; ($ last : expr $ (, ) ? ) => {$ last . spaces () ; vis ! () ; } ; ($ first : expr ; $ ($ rest : tt ) * ) => {$ first . lines () ; println ! () ; vis ! ($ ($ rest ) * ) ; } ; ($ first : expr => $ ($ rest : tt ) * ) => {$ first . continuous () ; vis ! ($ ($ rest ) * ) ; } ; ($ first : expr , $ ($ rest : tt ) * ) => {$ first . spaces () ; print ! (" " ) ; vis ! ($ ($ rest ) * ) ; } ; }
    #[test]
    fn vis_test() {
        let a = 1;
        let b = 2;
        let c = 3;
        let v = vec!['a', 'b', 'c'];
        vis!();
        vis!(a);
        vis!(a,);
        vis ! (a => );
        vis ! (a ; );
        println!();
        vis!(a + b);
        println!();
        vis!(v);
        vis!(v,);
        vis ! (v => );
        vis ! (v ; );
        println!();
        println!();
        vis!(a, b);
        vis!(a, b, c);
        vis ! (a , b , c ; );
        println!();
        vis ! (a => b );
        vis ! (a => b => c );
        vis ! (a => b => c , );
        println!();
        vis ! (a ; b );
        vis ! (a ; b ; c );
        vis ! (a ; b ; c => );
        println!();
        vis ! (a , b ; c );
        vis ! (a ; b , c );
        vis ! (a => b , c );
        vis ! (a ; b => c );
        println!();
        vis!(a, v);
        vis ! (a , v ; );
        vis ! (a => v => );
    }
}

pub use consts::*;
pub mod consts {
    pub const MOD10E9_7: usize = 1000000007; // 10 ^ 9 + 7
    pub const MOD99_: usize = 998244353;
    pub const MAX: usize = std::usize::MAX; // = 2 ^ 64 - 1 = 18446744073709551615 ≈ 1.8 * 10 ^ 19
    pub const INF: usize = 2000000000000000000; // MAX / 9 < 2 * 10e18 < MAX / 10
    pub const FNI: i64 = -2000000000000000000; // == -(INF as i64)
    pub const PI: f64 = std::f64::consts::PI; // 3.141592653589793 -- 10 ^ -15
    pub const ASCII_A_LARGE: u8 = 65;
    pub const ASCII_A_SMALL: u8 = 97;
    pub const ASCII_0: u8 = 48;
    pub const ADJ4: &[(isize, isize); 4] = &[(1, 0), (0, 1), (-1, 0), (0, -1)];
    pub const ADJ8: &[(isize, isize); 8] = &[
        (1, 0),
        (1, 1),
        (0, 1),
        (-1, 1),
        (-1, 0),
        (-1, -1),
        (0, -1),
        (1, -1),
    ];
}
#[derive(Debug, Clone)]
pub struct UnionFind {
    parent: Vec<usize>,
    size: Vec<usize>,
}
impl UnionFind {
    pub fn new(n: usize) -> Self {
        Self {
            parent: (0..n).collect::<Vec<usize>>(),
            size: vec![1; n],
        }
    }
    pub fn len(&self) -> usize {
        self.parent.len()
    }
    /// i が属する集合の親のインデックス
    pub fn find(&mut self, mut i: usize) -> usize {
        while self.parent[i] != i {
            self.parent[i] = self.find(self.parent[i]);
            i = self.parent[self.parent[i]];
        }
        i
    }
    /// a と b を繋ぐ
    pub fn unite(&mut self, a: usize, b: usize) -> bool {
        let mut a = self.find(a);
        let mut b = self.find(b);
        if a == b {
            return false;
        }
        if self.size[a] < self.size[b] {
            std::mem::swap(&mut a, &mut b);
        }
        self.size[a] += self.size[b];
        self.parent[b] = a;
        true
    }
    /// i が属する集合の要素数
    pub fn count(&mut self, i: usize) -> usize {
        let p = self.find(i);
        self.size[p]
    }
    /// a と b が同一集合に属するか
    pub fn joint(&mut self, a: usize, b: usize) -> bool {
        self.find(a) == self.find(b)
    }
    /// i が属する集合の要素を列挙する
    pub fn get_group(&mut self, i: usize) -> HashSet<usize> {
        let p = self.find(i);
        (0..self.len()).filter(|x| self.find(*x) == p).collect()
    }
}

// code...
// #[fastout]
fn main() {
    input! {
        n: usize, m: usize,
        ab: [(Usize1, Usize1); m]
    }

    let mut uf = UnionFind::new(n);
    for (i, j) in ab {
        uf.unite(i, j);
    }

    let mut ans = 0;
    for i in 0..n {
        let c = uf.count(i);
        ans = max(c, ans);
    }

    vis!(ans);
}