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 Java (OpenJDK 11.0.6) (judge verdict: Accepted)

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.NoSuchElementException;


public class Main {
	public static void main(String[] args) {
		
		for(int i=0;i<1;i++) {
			
			slover();
			out.flush();
		}
	}
	static FastScanner sc = new FastScanner();
	static PrintWriter out = new PrintWriter(System.out);
	

		



		private static class D{
		
			ArrayList<Integer> X=new ArrayList<>();
			ArrayList<Integer> Y=new ArrayList<>();
			int t=0;
			int index=0;
			D(ArrayList<Integer> X,ArrayList<Integer> Y,int t,int index){
				this.X=X;;
				this.Y=Y;
				this.t=t;
				this.index=index;
			}

		}



	//Set<Long> a=new HashSet<Long>();
	
	
	
	//Integer.toBinaryString(i);
	//Integer.toString(i, 2);
//	//Integer.parseInt(bin, 2);
	//bitset
		//StringBuffer S = new StringBuffer(sc.next());
				//String hoge2 = str.reverse().toString();
				//map.containsKey(A)
				//Map<Integer, Integer> map = new HashMap<Integer, Integer>(N);
				/*for ( キーのデータ型 key : マップの名前.keySet() ) {
					データのデータ型 data = マップの名前.get( key );

					// keyやdataを使った処理;
				}*/
				//int i = Integer.parseInt(s);
				//Queue<String> qq=new ArrayDeque<>(); //add,poll,peek BFSは前から実行される
				//Deque<String> dd=new ArrayDeque<>();//push後ろに入れる,poll(pop)後ろからとる,peek addは先頭に入るからバグ注意
				//stackdfsは後ろから実行される
				//ArrayList<Integer> cc = new ArrayList<>(N);
				//cc.contains(tmp)
				//Arrays.asList(c).contains("a")
				//list.set(1,"walk");//1 1 2 3 5
				//PriorityQueue<Integer> r=new PriorityQueue<Integer>();//add poll
	private static int mod=1000000007;
	private static int INF =999999999;
	private static long LINF=1000000000000000000L;
	private static int slover()   {
		
		int N=sc.nextInt(),M=sc.nextInt();
		int tree[]=new int[N+1];
		for(int i=0;i<=N;i++)tree[i]=i;
		for(int i=0;i<M;i++) {
			int a=sc.nextInt(),b=sc.nextInt();
			union(tree,a,b);
		}
		p(union_MaxSize(tree));
		
		
		
		
		
		
		
		
		
		return 0;
	}
	
	
	
	
	private static int union_MaxSize(int[] tree) {
		
	
		int V[]=new int[tree.length+1];
		
		for(int i=0;i<tree.length;i++) {
			V[find(tree,i)]++;
		}
		int max=0;
		for(int i=0;i<=tree.length;i++) {
			max=Math.max(max, V[i]);
		}
		return max;
	}
	
	public static int find(int[] tree,int idx) {
        if(tree[idx]==idx) return idx;
        else {
        	tree[idx] = find(tree,tree[idx]);
        	return tree[idx];
        }
    }
	public static void union_shape(int[] tree) {
		for(int i=0;i<tree.length;i++) {
			find(tree,i);
		}
	}

    //union idx2 tree to idx1 tree O(a(N))
    public static int[] union(int tree[],int idx1,int idx2) {
        int root1 = find(tree,idx1);
        int root2 = find(tree,idx2);
        tree[root2] = root1;
        return tree;
    }
	private static boolean heihou(long T) {
		if(Math.pow((int)Math.sqrt(T), 2)==T)return true;
		return false;
	}
	private static String zeroume(String S) {
		while(S.length()<6)S='0'+S;
		return S;
	}
	
	//速度が足りないときは、前計算を1回だけにしたり、longをintに変えたりするといい
	private static long modNcR2(int n,int r) {
		long x_b=1;
		
		for(int i=0;i<r;i++) {
			x_b*=(n-i);
			x_b%=mod;
		}
		
		long y=1;
		for(int i=1;i<=r;i++) {
			y*=i;
			y%=mod;
			//nPrにr!%modの逆元を掛ける
		}
		return  (x_b*modPow((int) y,mod-2,mod)%mod);
		
	}
	private static int modPow2(String S,int mod) {
		//iのt乗をO(log t)で返す
		int a=2;
		int res=1;
		int N=S.length()-1;
		for(int i=0;i<S.length();i++) {
			if(S.charAt(N-i)=='1') {
				res=res*a%mod;
			}
			a=a*a%mod;
		}
		return res;
	}
	

	private static ArrayList<Long> divisors2(long t) {
		ArrayList<Long> c=new ArrayList<>();
		for(long i=2;i*i<=t;i++) {
			
			
			if(t%i==0) {
				p(t+" "+i);
				c.add((long)i);
				t/=i;
				i--;
			}
		}
		if(t!=1)c.add(t);
		Collections.sort(c);
		
		return c;
	}
	
	
	private static long modPow(int i,int t,long mod) {
		//iのt乗をO(log t)で返す
		long a=i;
		long res=1;
		while(t!=0) {
			//p(t);
			if((1&t)==1) {
				res=res*a%mod;
			}
			a=a*a%mod;
			t=t>>1;
		}
		return res;
	}

	public static long gcd(long num1,long num2) {
        if(num2==0) return num1;
        else return gcd(num2,num1%num2);
    }
	public static long lcm(long num1,long num2) {
		return num1*num2/gcd(num1,num2);
	}
	//O(N^0.5)
	private static boolean isPrime(long t) {
		if(t<2)return false;
		for(int i=2;i*i<=t;i++) {
			if(t%i==0)return false;
		}
		return true;
	}
	private static ArrayList<Long> divisors(long t) {
		ArrayList<Long> c=new ArrayList<>();
		
		for(long i=1;i*i<=t;i++) {
			if(t%i==0) {
				
				if(i*i!=t) {
					c.add(t/i);
				}
				c.add((long)i);
			}
		}
		return c;
	}
	private static void bubunwa() {
		int N=sc.nextInt();
		int K=sc.nextInt();
		int a[]=sc.nextIntArray(N, false);

		boolean dp[] =new boolean[K+1];

		Arrays.fill(dp, false);
		dp[0]=true;
		for(int i=0;i<N;i++) {
			for(int x=K-a[i];x>=0;x--) {
				if(dp[x])dp[x+a[i]]=true;
			}
		}

		p(dp[K] ? "Yes":"No");
	}
	
	private static String bitToString(int i) {
		
			String T="";
			for(int j=0;j<2;j++) {
				//if(i>>j==0)break;
				if((1&i>>j)==1) {
					T+="1";
				}else T+="0";
			}
			return (i+" "+T);

		

	}
/*********************************************************************/
	//target以下までの値のindexを返す
	//target以上が欲しいときは返り値+1すればいい
	//0-indexで個数を求めるときはさらにindex+1する必要あり
	private static int lower_bound(long a[],long target) {
	//p(target+ " "+Arrays.toString(a));
	if(a[0]>target)return -1;
	//最後の値がtarget以下であるときは最後のindexを返す。
	//target以上が欲しいときは注意する必要あり
	if(a[a.length-1]<=target) return a.length-1;

	int S=-1;
	int E=a.length;

	while(S<E) {

		int G=(S+E)/2;
		if(G+1==a.length) {
			G--;
		}
		//未満にしたいときは=の場所を変える
		if(a[G]<=target&&a[G+1]>target) {
			return G;

		}else if(a[G]<=target){
			S=G;
		}else if(a[G]>target) {
			E=G;
		}

	}


	return -1;
	}
	static String nextPermutation(String s) {
		ArrayList<Character> list=new ArrayList<>();
		for(int i=0;i<s.length();i++) {
			list.add(s.charAt(i));
		}
		int pivotPos=-1;
		char pivot=0;
		for(int i=list.size()-2;i>=0;i--) {
			if(list.get(i)<list.get(i+1)) {
				pivotPos=i;
				pivot=list.get(i);
				break;
			}
		}
		if(pivotPos==-1&&pivot==0) {
			return "Final";
		}
		int L=pivotPos+1,R=list.size()-1;



		int minPos=-1;
		char min =Character.MAX_VALUE;

		for(int i=R;i>=L;i--) {
			if(pivot<list.get(i)) {
				if(list.get(i)<min) {
					min=list.get(i);
					minPos=i;
				}
			}
		}

		Collections.swap(list, pivotPos, minPos);
		Collections.sort(list.subList(L, R+1));


		StringBuilder sb=new StringBuilder();
		for(int i=0;i<list.size();i++) {
			sb.append(list.get(i));
		}
		return sb.toString();
	}
private static long[][] com;
private static void nCr() {
	int MAX = 3001;
	com= new long[MAX][MAX];
	for(int i = 0; i < MAX; i++)
	    com[i][0] = 1;
	for(int i = 1; i < MAX; i++) {
	    for(int j = 1; j <= i; j++) {
	        com[i][j] = com[i-1][j-1] + com[i-1][j];
	        com[i][j] %= mod;
	    }
	}
}


	static void p(String ans) {out.println(ans);}
	static void p(int ans) {out.println(ans);}
	static void p() {out.println();}
	static void p(long ans) {out.println(ans);}
	static void p(double ans) {out.println(ans);}
	static void p(boolean ans) {out.println(ans);}
	
	
	static void pn(String ans) {out.print(ans);};
	static void pn(int ans) {out.print(ans);};
	static void pn() {out.print("");};
	static void pn(long ans) {out.print(ans);};
	static void pn(double ans) {out.print(ans);};

	static class FastScanner {
	    private final InputStream in = System.in;
	    private final byte[] buffer = new byte[1024];
	    private int ptr = 0;
	    private int buflen = 0;

	    private boolean hasNextByte() {
	      if (ptr < buflen) {
	        return true;
	      } else {
	        ptr = 0;
	        try {
	          buflen = in.read(buffer);
	        } catch (IOException e) {
	          e.printStackTrace();
	        }
	        if (buflen <= 0) {
	          return false;
	        }
	      }
	      return true;
	    }

	    private int readByte() {
	      if (hasNextByte()) return buffer[ptr++];
	      else return -1;
	    }

	    private static boolean isPrintableChar(int c) {
	      return 33 <= c && c <= 126;
	    }

	    private void skipUnprintable() {
	      while (hasNextByte() && !isPrintableChar(buffer[ptr])) ptr++;
	    }

	    public boolean hasNext() {
	      skipUnprintable();
	      return hasNextByte();
	    }

	    public String next() {
	      if (!hasNext()) throw new NoSuchElementException();
	      StringBuilder sb = new StringBuilder();
	      int b = readByte();
	      while (isPrintableChar(b)) {
	        sb.appendCodePoint(b);
	        b = readByte();
	      }
	      return sb.toString();
	    }

	    public long nextLong() {
	      if (!hasNext()) throw new NoSuchElementException();
	      long n = 0;
	      boolean minus = false;
	      int b = readByte();
	      if (b == '-') {
	        minus = true;
	        b = readByte();
	      }
	      if (b < '0' || '9' < b) {
	        throw new NumberFormatException();
	      }
	      while (true) {
	        if ('0' <= b && b <= '9') {
	          n *= 10;
	          n += b - '0';
	        } else if (b == -1 || !isPrintableChar(b)) {
	          return minus ? -n : n;
	        } else {
	          throw new NumberFormatException();
	        }
	        b = readByte();
	      }
	    }

	    public int nextInt() {
	      return (int) nextLong();
	    }

	    public double nextDouble(){
	    	return Double.parseDouble(next());
	    }

	    public int[] nextIntArray(int N, boolean oneBased) {
	      if (oneBased) {
	        int[] array = new int[N + 1];
	        for (int i = 1; i <= N; i++) {
	          array[i] = sc.nextInt();
	        }
	        return array;
	      } else {
	        int[] array = new int[N];
	        for (int i = 0; i < N; i++) {
	          array[i] = sc.nextInt();
	        }
	        return array;
	      }
	    }

	    public long[] nextLongArray(int N, boolean oneBased) {
	      if (oneBased) {
	        long[] array = new long[N + 1];
	        for (int i = 1; i <= N; i++) {
	          array[i] = sc.nextLong();
	        }
	        return array;
	      } else {
	        long[] array = new long[N];
	        for (int i = 0; i < N; i++) {
	          array[i] = sc.nextLong();
	        }
	        return array;
	      }
	    }
	  }
}