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-----
5 3
1 2
3 4
5 1

-----Sample Output-----
3

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

Difficulty: interview

Starter code:

Solutions:
import sys,math,collections,itertools
input = sys.stdin.readline

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

N,M = list(map(int,input().split()))
uf = UnionFind(N)
for _ in range(M):
    A,B = list(map(int,input().split()))
    A -=1
    B -=1
    uf.union(A,B)
ans = 0
for i in range(N):
    ans = max(ans,uf.size(i))
print(ans)


class UnionFind():
    # 作りたい要素数nで初期化
    # 使用するインスタンス変数の初期化
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
    # ノードxのrootノードを見つける
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def unite(self, x, y):
    # 木の併合、入力は併合したい各ノード⇒(a,b)
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
    # ノードxが属する木のサイズを返す
        return -self.parents[self.find(x)]

    def same(self, x, y):
    # 入力ノード(x,y)が同じグループに属するかを返す
        return self.find(x) == self.find(y)

    def members(self, x):
    #ノードxが属するメンバーをリスト形式で返す
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
    #親全てをリスト形式で返す
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
    #グループ数の合計を返す
        return len(self.roots())

import sys
sys.setrecursionlimit(10**9)
input = sys.stdin.readline



import sys
sys.setrecursionlimit(10**9)
input = sys.stdin.readline

n,m=list(map(int,input().split()))
u=UnionFind(n)
for i in range(m):
    a,b=list(map(int,input().split()))
    a-=1
    b-=1
    u.unite(a,b)
ans=0
for i in range(n):
    ans=max(ans,u.size(i))
print(ans)


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

N,M = map(int,input().split())
UF = UnionFind(N+1)
for i in range(M):
    x,y = map(int,input().split())
    UF.union(x,y)
ls = UF.roots()
ls.pop(0)
ls2 = []
for i in ls:
    ls2.append(UF.size(i))
print(max(ls2))

class UnionFind:
    from typing import List, Set
    def __init__(self, n):
        self.n = n
        self.parent = [-1] * n
    def merge(self, x, y) -> int:
        x = self.leader(x)
        y = self.leader(y)
        if x == y:
            return 0
        if self.parent[x] > self.parent[y]:
            x, y = y, x
        self.parent[x] += self.parent[y]
        self.parent[y] = x
        return self.parent[x]
    def same(self, x, y) -> bool:
        return self.leader(x) == self.leader(y)
    def leader(self, x) -> int:
        if self.parent[x] < 0:
            return x
        else:
            self.parent[x] = self.leader(self.parent[x])
            return self.parent[x]
    def size(self, x) -> int:
        return -self.parent[self.leader(x)]
    def groups(self) -> List[Set[int]]:
        groups = dict()
        for i in range(self.n):
            p = self.leader(i)
            if not groups.get(p):
                groups[p] = set()
            groups[p].add(i)
        return list(groups.values())

n,m=map(int,input().split())
uf = UnionFind(n)
for i in range(m):
    a,b=map(int,input().split())
    uf.merge(a-1,b-1)
grp=uf.groups()
ans=0
for i in range(len(grp)):
    if (len(grp[i])>ans):
        ans=len(grp[i])
print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
        

N, M = map(int, input().split())

uf = UnionFind(N+1)
for i in range(M):
    A, B = map(int, input().split())
    uf.union(A, B)

ans = 0
for i in range(N+1):
    ans = max(ans, uf.size(i))

print(ans)

import sys
sys.setrecursionlimit(10 ** 9)

n, m = map(int, input().split())
root = [-1] * n

def r(x):
    if root[x] < 0:
        return x
    else:
        root[x] = r(root[x])
        return root[x]

def unite(x, y):
    x = r(x)
    y = r(y)
    if x == y:
        return
    root[x] += root[y]
    root[y] = x

def size(x):
    x = r(x)
    return -root[x]

for i in range(m):
    x, y = map(int, input().split())
    x -= 1
    y -= 1
    unite(x, y)

max_size = max(map(size, range(n)))

print(max_size)

# n, m = map(int, input().split())
# graph = [[] for i in range(n+1)]

# for i in range(m):
#     a, b = map(int, input().split())
#     graph[a] += [b]
#     graph[b] += [a]
# print(graph)
# nex = [i for i in range(1, n+1)]
# tmp = []
# ans = 0
# for i in range(1, n+1):
#     if i in nex:
#         for j in graph[i]:
#             if j in nex:
#                 tmp.append(j)
#         ans += 1
#         print(tmp)
#         nex = tmp
# print(ans)




#O(α(n)) < O(log(n))
class UnionFind():

    def __init__(self, n):
        self.par = []
        self.rank = []
        for i in range(n):
            self.par.append(i)#自身を親としてinit
            self.rank.append(1)#１つのノードをrank1としてカウント

    def find(self, x):
        if self.par[x]==x:
            return x
        else:
            self.par[x] = self.find(self.par[x])#親の更新も同時に行う
            return self.par[x]

    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x != y:
            if self.rank[x] <= self.rank[y]:
                x, y = y, x

            #rankが小さい方を大きい方につなげる
            self.par[y] = x

            #親のrankに繋いだ木のrankを全て足す
            self.rank[x] += self.rank[y]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def size(self, x):
        return self.rank[self.find(x)]

n, m = map(int, input().split())
ab = [list(map(int, input().split())) for _ in range(m)]

ans = 0
uf = UnionFind(n)

for a, b in ab:
    uf.unite(a-1, b-1)

for i in range(n):
    ans = max(ans, uf.size(i))

print(ans)

n,m=map(int,input().split())
l=[]
for i in range(m):
    a,b=map(int,input().split())
    l.append([a,b])

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x
    def size(self, x):
        return -self.parents[self.find(x)]

uf=UnionFind(n)

for i in range(m):
    uf.union(l[i][0]-1,l[i][1]-1)

s=[]
for i in range(n):
    s.append(uf.size(i))

print(max(s))

class UnionFind():
  def __init__(self,n):
    self.n = n
    self.parents = [-1] * (n+1)
  
  def find(self, x):
    if self.parents[x] < 0:
      return x
    self.parents[x] = self.find(self.parents[x])
    return self.parents[x]
  
  def union(self,x,y):
    x = self.find(x)
    y = self.find(y)
    if x == y:
      return
    if self.parents[x] > self.parents[y]:
      x,y = y,x
    self.parents[x] += self.parents[y]
    self.parents[y] = x

  def size(self, x):
    return -1 * self.parents[x]
  
  def same(self, x, y):
    return self.find(x) == self.find(y)
  
  def members(self, x):
    root = self.find(x)
    return [i for i in range(self.n) if self.find(i) == root]
    
  def roots(self):
    return [i for i,x in enumerate(self.parents) if x < 0]
    
  def group_count(self):
    return len(self.roots())

N,M = map(int,input().split())
uf = UnionFind(N)
for i in range(M):
  a,b = map(int, input().split())
  uf.union(a,b)
  
ans = 0
R = uf.roots()
for r in R:
  ans = max(ans, uf.size(r))
print(ans)

def find(x):
  if par[x] < 0:
    return x
  else:
    tank = []
    while par[x] >= 0:
      tank.append(x)
      x = par[x]
    for elt in tank:
      par[elt] = x
    return x

def unite(x,y):
  x = find(x)
  y = find(y)
    
  if x == y:
    return False
  else:
    if par[x] > par[y]:
      x,y = y,x
    par[x] += par[y]
    par[y] = x
    return True

def same(x,y):
  return find(x) == find(y)

def size(x):
  return -par[find(x)]

N,M=map(int,input().split())
par = [-1]*N
for _ in range(M):
  a,b=map(int,input().split())
  unite(a-1,b-1)
print(-min(par))

N, M = list(map(int, input().split()))


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


u = UnionFind(N)
for _ in range(M):
    a, b = list(map(int, input().split()))
    u.union(a - 1, b - 1)

ans = 0
for i in u.roots():
    ans = max(u.size(i), ans)

print(ans)


class UnionFind():
  def __init__(self,n):
    self.n = n
    self.parents = [-1] * (n+1)
    
  def find(self, x):
    if self.parents[x] < 0:
      return x
    self.parents[x] = self.find(self.parents[x])
    return self.parents[x]
  
  def union(self,x,y):
    x = self.find(x)
    y = self.find(y)
    if x == y:
      return
    if self.parents[x] > self.parents[y]:
      x,y = y,x
    self.parents[x] += self.parents[y]
    self.parents[y] = x
    
  def size(self, x):
    return -1 * self.parents[x]
  
  def same(self,x,y):
    return self.find(x) == self.find(y)
  
  def members(self,x):
    root = self.find(x)
    return [i for i in range(self.n) if self.find(i) == root]
    
  def roots(self):
    return [i for i in range(1,self.n+1,1) if self.parents[i] < 0]
  
  def group_count(self):
    return len(self.roots())
  
N,M = map(int,input().split())
uf = UnionFind(N)
for i in range(M):
  a,b = map(int,input().split())
  uf.union(a,b)
ans = 0
for r in uf.roots():
  ans = max(ans,uf.size(r))
print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * (n)
        
    def find(self, x):
        if self.parents[x] < 0:     # 根ならその番号を返す
            return x
        else:                       # 根でないならその要素で再検索
            self.parents[x] = self.find(self.parents[x])    # 走査過程で親を書き換え
            return self.parents[x]
        
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:                  # 既に同じ木に属している場合
            return
        
        # 違う木に属している場合は高い方の木に低い方を併合
        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]  # 併合される側の要素数を加算
        self.parents[y] = x                 # 併合する側の親を書き換え
    
    def size(self, x):
        return -self.parents[self.find(x)]
    
    def issame(self, x, y):
        return self.find[x] == self.find(y)


'''
参考
https://qiita.com/K_SIO/items/03ff1fc1184cb39674aa#d-friends
'''
def __starting_point():
    N, M = list(map(int, input().split()))
    uf = UnionFind(N)

    for _ in range(M):
        A, B = list(map(int, input().split()))
        uf.union(A-1, B-1)
    
    ans = 0

    for i in range(N):
        ans = max(ans, uf.size(i))
    
    print(ans)

__starting_point()

import sys
sys.setrecursionlimit(10**7)

def dfs(v, visited, counter):
    visited[v] = 1
    for nv in G[v]:
        if visited[nv]:
            continue
        visited[nv] = 1
        counter.append(1)
        dfs(nv, visited, counter)
    return

n, m = map(int, input().split())
G = [[] for _ in range(n)]
visited = [0] * n

for i in range(m):
    a, b = map(int, input().split())
    G[a - 1].append(b - 1)
    G[b - 1].append(a - 1)

max_friends = 0
for i in range(n):
    if visited[i]:
        continue
    friends = []
    dfs(i, visited, friends)
    max_friends  = max(max_friends, sum(friends) + 1)

print(max_friends)

from collections import deque
def bfs(edges,start):
    waiting = deque()
    done[start] = 2
    c = 1
    for n in edges[start]:
        done[n] = 1
        waiting.append(n)
    while len(waiting):
        cur_node = waiting.popleft()
        if done[cur_node] != 2:
            done[cur_node] = 2
            c += 1
            for n in edges[cur_node]:
                if done[n] != 2:
                    done[n] = 1
                    waiting.append(n)
    #print(done,c)
    return c

N,M = [int(a) for a in input().split()]
edge = [[] for _ in range(N)] 
for _ in range(M):
    a,b = [int(a) for a in input().split()]
    edge[a-1].append(b-1)
    edge[b-1].append(a-1)
done = [0]*N
ans = 0
for i in range(N):
  if done[i]==0: ans = max(ans, bfs(edge,i))
print(ans)

import sys


def input():
    return sys.stdin.readline()[:-1]


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


N, M = list(map(int, input().split()))
uf = UnionFind(N)
for _ in range(M):
    a, b = list(map(int, input().split()))
    a -= 1
    b -= 1
    uf.union(a, b)

ans = 0
for i in range(N):
    ans = max(ans, uf.size(i))
print(ans)


class UnionFind(): # 0インデックス
  def __init__(self, n):
    self.n = n
    self.parents = [-1] * n
    
  def find(self, x):
    if self.parents[x] < 0:
      return x
    else:
      self.parents[x] = self.find(self.parents[x])
      return self.parents[x]

  def union(self, x, y):
    x = self.find(x)
    y = self.find(y)

    if x == y:
      return

    if self.parents[x] > self.parents[y]:
      x, y = y, x

    self.parents[x] += self.parents[y]
    self.parents[y] = x

  def size(self, x):
    return -self.parents[self.find(x)]

N, M = map(int, input().split()) # N人、M個の関係
union_find = UnionFind(N)
for _ in range(M):
  A, B = map(int, input().split())
  A -= 1
  B -= 1
  union_find.union(A, B)

answer = 0
for i in range(N):
  answer = max(answer, union_find.size(i))

print(answer)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
      
      
N, M = map(int,input().split())
uf = UnionFind(N)
for i in range(M):
  A, B = map(int,input().split())
  A -= 1
  B -= 1
  uf.union(A, B)
  
L = [0] * N
for i in range(N):
  L[i] = uf.size(i)
  
print(max(L))

from collections import Counter

N, M = map(int, input().split())
AB = [map(int, input().split()) for _ in range(M)]

F = list(range(N+1))
for A, B in AB:
    H = []
    while F[A] != A:
        H.append(A)
        A = F[A]
    while F[B] != B:
        H.append(B)
        B = F[B]
    for h in H+[B]:
        F[h] = A

for i, f in enumerate(F):
    H = [i]
    while F[f] != f:
        H.append(f)
        f = F[f]
    for h in H:
        F[h] = f

print(Counter(F).most_common(1)[0][1])

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
        self.size=[1]*n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.size[x]+=self.size[y]
        self.size[y]=self.size[x]
        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

n,m=map(int,input().split())
uf = UnionFind(n)
for i in range(m):
    a,b=map(int,input().split())
    uf.union(a-1,b-1)
print(max(uf.size))

from collections import deque

n,m = list(map(int, input().split()))
G = [[] for _ in range(n)]

for i in range(m):
    a,b = list(map(int, input().split()))
    a -= 1
    b -= 1
    G[a].append(b)
    G[b].append(a)

que = deque()
dist = [-1]*n
ans = 0
for i in range(n):
    if dist[i]!=-1:
        continue
    dist[i]==0
    que.append(i)
    cnt = 0
    while len(que):
        v = que.popleft()
        vs = G[v]
        for nv in vs:
            if dist[nv]==-1:
                dist[nv] = dist[v]+1
                cnt += 1
                que.append(nv)
    ans = max(cnt, ans)
if m==0:
    print((1))
else:
    print(ans)


class Unionfind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * (n+1)
        
    def find(self, x):
        if(self.parents[x] < 0):
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
        
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        
        if(x == y):
            return
        
        if(self.parents[x] > self.parents[y]):
            x, y = y, x
            
        self.parents[x] += self.parents[y]
        self.parents[y] = x
    
    def size(self, x):
        return -self.parents[self.find(x)]
    
    def same(self, x, y):
        return self.find(x) == self.find(y)
    
    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]
    
    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]
    
    def group_count(self):
        return len(self.roots())
    
    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}
    
    def __str__(self):
        return '\n'.join('{}:{}'.format(r, self.members(r)) for r in self.roots())

N, M = map(int, input().split())
uf = Unionfind(N)

for _ in range(M):
    a, b = map(int, input().split())
    a -= 1
    b -= 1
    uf.union(a, b)

ans = [uf.size(i) for i in range(N)]
print(max(ans))

class UnionFind:
    def __init__(self, n=0):
        self.d = [-1]*n
    def find(self, x):
        if self.d[x] < 0:
            return x
        else:
            self.d[x] = self.find(self.d[x])
            return self.d[x]
    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return False
        if self.d[x] > self.d[y]:
            x, y = y, x
        self.d[x] += self.d[y]
        self.d[y] = x
        return True
    def same(self, x, y):
        return self.find(x) == self.find(y)
    def size(self, x):
        return -self.d[self.find(x)]


n, m = map(int, input().split())
uf = UnionFind(n)

for _ in range(m):
    a, b = map(int, input().split())
    uf.unite(a-1, b-1)

ans = 0
for i in range(n):
    ans = max(ans, uf.size(i))

print(ans)

import networkx as nx
import sys

G = nx.Graph()

n, m = list(map(int, input().split()))
if m == 0:
    print((1))
    return
for i in range(m):
    a, b = list(map(int, input().split()))
    G.add_edge(a, b)

largest_cc = max(nx.connected_components(G), key=len)
print((len(largest_cc)))


import sys


def input():
    return sys.stdin.readline().strip()


sys.setrecursionlimit(20000000)

MOD = 10 ** 9 + 7
INF = float("inf")


class UnionFind:
    # 初期化
    def __init__(self, n_nodes):
        self.n_nodes = n_nodes
        # 親要素のノード番号を格納する.初めは全て親ノード.
        # self.parent[x] == x の時，そのノードは根.
        self.parent = [i for i in range(n_nodes)]
        # 木の高さを格納する．
        self.rank = [1] * n_nodes
        # 自分と同じグループの要素数を格納．親要素を参照するようにする
        self.size = [1] * n_nodes

    # 検索. 根を返す.
    def find(self, x):
        if self.parent[x] == x:
            return x
        else:
            # 一度調べた値は，根に繋ぎ直す.
            self.parent[x] = self.find(self.parent[x])
            return self.parent[x]

    # 併合.
    # 木の高さが低くなるように.
    # 親要素の書き換えは少ない方がいい
    def unite(self, x, y):
        # 根を探す．
        x = self.find(x)
        y = self.find(y)
        # 根が同じ場合はそのまま
        if x == y:
            return
        # 木の高さを比較し，低い方を高い方の根に貼る.
        if self.rank[x] > self.rank[y]:
            self.parent[y] = x
            self.size[x] += self.size[y]
        else:
            self.parent[x] = y
            self.size[y] += self.size[x]
            if self.rank[x] == self.rank[y]:
                self.rank[y] += 1

    # 同じ集合に属するか判定
    def check(self, x, y):
        return self.find(x) == self.find(y)

    # 同一グループに所属する要素数を返す
    def get_size(self, x):
        return self.size[self.find(x)]

    # 親要素のリストを返す
    def get_parent_list(self):
        return [i for i in range(self.n_nodes) if self.find(i) == i]

    # グループの数を返す
    def get_n_groups(self):
        return len(self.get_parent_list())

    # 同じグループに所属する要素の集合を返す
    def get_members(self, x):
        parent = self.find(x)
        return [i for i in range(self.n_nodes) if self.find(i) == parent]

    # 全ての根に対する，グループのメンバーを返す．
    def get_members_dict(self):
        return {par: self.get_members(par) for par in self.get_parent_list()}


def main():
    N, M = list(map(int, input().split()))
    edge = [set(map(int, input().split())) for _ in range(M)]

    UF = UnionFind(N)
    for x, y in edge:
        UF.unite(x - 1, y - 1)

    parents = UF.get_parent_list()
    answer = 0
    for p in parents:
        size = UF.get_size(p)
        answer = max(answer, size)
    print(answer)


def __starting_point():
    main()

__starting_point()

N,M = map(int, input().split())

class UnionFind:
    def __init__(self, N):
        self.root = list(range(N + 1))
        self.size = [1] * (N + 1)

    def __getitem__(self, x):
        root = self.root
        while root[x] != x:
            root[x] = root[root[x]]
            x = root[x]
        return x

    def merge(self, x, y):
        x = self[x]
        y = self[y]
        if x == y:
            return
        sx, sy = self.size[x], self.size[y]
        if sx < sy:
            x, y = y, x
            sx, sy = sy, sx
        self.root[y] = x
        self.size[x] += sy

    def find_max(self):
        return max(self.size)

uf = UnionFind(N)

answer = []
append = answer.append
merge = uf.merge

for i in range(M):
    a,b = map(int, input().split())
    merge(a,b)
print(uf.find_max())

from collections import deque 

n, m = map(int, input().split())
G = [[] for _ in range(n)]

for i in range(m):
    a, b = map(int, input().split())
    G[a - 1].append(b - 1)
    G[b - 1].append(a - 1)

visited = [0] * n

def friends(v):
    stack = deque([v])
    visited[v] = 1
    cnt = 1
    while stack:
        v = stack.popleft()
        for nv in G[v]:
            if visited[nv]:
               continue
            visited[nv] = 1
            stack.append(nv)
            cnt += 1
    return cnt

max_friends = 0
for i in range(n):
    if visited[i]:
        continue
    max_friends  = max(max_friends, friends(i))

print(max_friends)

class UnionFind:
    def __init__(self, n):
        self.n = n
        self.par = [i for i in range(n)]
        self.rank = [0 for i in range(n)]
        self.siz = [1 for i in range(n)]

    def find(self, x):
        if self.par[x] == x:
            return x
        self.par[x] = self.find(self.par[x])
        return self.find(self.par[x])

    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.rank[x] > self.rank[y]:
            self.par[y] = x
            self.siz[x] += self.siz[y]
        else:
            self.par[x] = y
            self.siz[y] += self.siz[x]
            if self.rank[x] == self.rank[y]:
                self.rank[y] += 1
    
    def same(self, x, y):
        return self.find(x) == self.find(y)

    def size(self, x):
        return self.siz[self.find(x)]
    
n, m = map(int, input().split())
uf = UnionFind(n)
for i in range(m):
    a, b = map(int, input().split())
    a -= 1
    b -= 1
    uf.unite(a, b)

res = 0
for i in range(n):
    res = max(res, uf.size(i))

print(res)

import sys
sys.setrecursionlimit(1000000)


f=lambda:map(int,input().split())
N,M=f()

if M==0:
  print(1)
  return

G=[set() for _ in range(N+1)]
for _ in range(M):
    a,b=f()
    G[a].add(b)
    G[b].add(a)
# print(G)

d={}
F=[0]*(N+1)

def dfs(i,n):
 if F[i]:return
 F[i]=1
 for g in G[i]:
    if F[g]:continue
    t=d.get(n, set())
    t.add(g)
    d[n]=t
    dfs(g,n)
   
for i in range(1,N+1):
  dfs(i,i)

print(max(len(i) for i in d.values())+1)

class UnionFind:
    from typing import List, Set

    def __init__(self, n):
        self.n = n
        self.parent = [-1] * n

    def merge(self, x, y) -> int:
        x = self.leader(x)
        y = self.leader(y)

        if x == y:
            return 0

        if self.parent[x] > self.parent[y]:
            x, y = y, x

        self.parent[x] += self.parent[y]
        self.parent[y] = x

        return self.parent[x]

    def same(self, x, y) -> bool:
        return self.leader(x) == self.leader(y)

    def leader(self, x) -> int:
        if self.parent[x] < 0:
            return x
        else:
            self.parent[x] = self.leader(self.parent[x])
            return self.parent[x]

    def size(self, x) -> int:
        return -self.parent[self.leader(x)]

    def groups(self) -> List[Set[int]]:
        groups = dict()

        for i in range(self.n):
            p = self.leader(i)
            if not groups.get(p):
                groups[p] = set()
            groups[p].add(i)

        return list(groups.values())

n,m=map(int,input().split())
uf = UnionFind(n)
for i in range(m):
    a,b=map(int,input().split())
    uf.merge(a-1,b-1)
grp=uf.groups()
ans=0
for i in range(len(grp)):
    if (len(grp[i])>ans):
        ans=len(grp[i])
print(ans)

import sys
sys.setrecursionlimit(10 ** 9)

N, M = map(int, input().split())
root = [-1] * N

def find(x):
    if root[x] < 0:
        return x
    else:
        root[x] = find(root[x])
        return root[x]

def union(x, y):
    x = find(x)
    y = find(y)
    if x == y:
        return
    root[x] += root[y]
    root[y] = x

def size(x):
    x = find(x)
    return -root[x]

for i in range(M):
    A, B = map(int, input().split())
    A -= 1
    B -= 1
    union(A, B)

ans = max(map(size, range(N)))
print(ans)

class UnionFind():
    def __init__(self, N):
        self.par = [-1] * N
        
    def root(self, x):
        if self.par[x] < 0: return x
        self.par[x] = self.root(self.par[x])
        return self.par[x]
        
    def unite(self, x, y):
        rx = self.root(x)
        ry = self.root(y)
        #print(f'root: {rx} {ry}')
        if rx == ry: return
        
        self.par[ry] += self.par[rx]
        self.par[rx] = ry
        

        
N, M = map(int, input().split())
uf = UnionFind(N)
for i in range(M):
    A, B = map(lambda x: int(x), input().split())
    A, B = A-1, B-1
    #print(f'{A} {B} {uf.par}')
    uf.unite(A,B)
    #print(f'{A} {B} {uf.par}')
    
print(-min(uf.par))

from typing import List


class DSU:
    def __init__(self, n: int = 0) -> None:
        self._n: int = n
        # root node: -1 * component size
        # otherwise: parent
        self.parent_or_size: List[int] = [-1] * n

    def merge(self, a: int, b: int) -> int:
        assert 0 <= a < self._n
        assert 0 <= b < self._n
        x, y = self.leader(a), self.leader(b)
        if x == y:
            return x
        if -self.parent_or_size[x] < -self.parent_or_size[y]:
            x, y = y, x
        self.parent_or_size[x] += self.parent_or_size[y]
        self.parent_or_size[y] = x
        return x

    def same(self, a: int, b: int) -> bool:
        assert 0 <= a < self._n
        assert 0 <= b < self._n
        return self.leader(a) == self.leader(b)

    def leader(self, a: int) -> int:
        assert 0 <= a < self._n
        if self.parent_or_size[a] < 0:
            return a
        self.parent_or_size[a] = self.leader(self.parent_or_size[a])
        return self.parent_or_size[a]


N, M, *AB = list(map(int, open(0).read().split()))
d = DSU(N)
for a, b in zip(*[iter(AB)] * 2):
    d.merge(a - 1, b - 1)
print((-min(d.parent_or_size)))


import sys
sys.setrecursionlimit(10 ** 9)

n, m = map(int, input().split())
p = [-1] * n
 
def find(x):
    if p[x] < 0:
        return x
    else:
        p[x] = find(p[x])
        return p[x]
 
def unite(x, y):
    x = find(x)
    y = find(y)
 
    if x == y:
        return
    
    p[x] += p[y]
    p[y] = x
 
def size(x):
    x = find(x)
    return -p[x]
 
for _ in range(m):
    x, y = map(int, input().split())
    x -= 1
    y -= 1
    unite(x, y)
 
max_size = max(map(size, range(n)))
print(max_size)

import sys
sys.setrecursionlimit(10**9)

n, m = map(int, input().split())
pair = [-1] * n
max_size = 0

def find(x):
    if pair[x] < 0:
        return x
    else:
        pair[x] = find(pair[x])
        return pair[x]

def unite(x, y):
    x = find(x)
    y = find(y)

    if x == y:
        return

    pair[x] += pair[y]
    pair[y] = x

def size(x):
    x = find(x)
    return -pair[x]

for _ in range(m):
    x, y = map(int, input().split())
    x -= 1
    y -= 1
    unite(x, y)

max_size = max(map(size, range(n)))
print(max_size)

import sys

sys.setrecursionlimit(1500)

class UnionFind:
    def __init__(self, N):
        # 最初はすべて根で初期化
        self.par = [-1 for i in range(N)]

    def root(self, x):
        # print(x, self.par)
        if self.par[x] == x:
            return x
        elif self.par[x] < 0:
            # par = self.root(self.par[x])
            # self.par[x] = x
            return x
        else:
            par = self.root(self.par[x])
            self.par[x] = par
            return par
        
    def unite(self, x, y):
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return
        if self.par[x] > self.par[y]:
            x, y = y, x
        self.par[x] += self.par[y]
        self.par[y] = x
        return True

    def same(self, x, y):
        rx = self.root(x)
        ry = self.root(y)
        return rx == ry

    def size(self, x):
        return -self.par[self.root(x)]

N,M=list(map(int,input().split()))


# union find構造を作る
uf = UnionFind(N)
for _ in range(M):
    A,B=[int(x)-1 for x in input().split()]
    uf.unite(A,B)

# r = [uf.size(n) for n in range(N)]
# print(uf.par)
print((-min(uf.par)))
# print(uf.par)


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x) -> int:
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y) -> None:
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x) -> int:
        return -self.parents[self.find(x)]

    def same(self, x, y) -> bool:
        return self.find(x) == self.find(y)

    def members(self, x) -> list:
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self) -> list:
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self) -> int:
        return len(self.roots())

    def all_group_members(self) -> dict:
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
    def solve(self):
        print(max([self.size(i) for i in self.roots()]))
n,m=map(int,input().split())
a=UnionFind(n)
for i in range(m):
    c,b=map(int,input().split())
    a.union(c-1,b-1)
a.solve()

class Unionfind:
    def __init__(self,n):
        self.par=[x for x in range(n)]
        self.num=[1]*n

    def root(self,a):
        if self.par[a]==a : return a
        parent=self.root(self.par[a])
        self.par[a]=parent
        return parent

    def unite(self,a,b):
        ra,rb=self.root(a),self.root(b)
        self.par[rb]=ra
        self.num[ra]+=self.num[rb]

    def same(self,a,b):
        return self.root(a)==self.root(b)



def main():
    n,m=map(int,input().split())
    uf=Unionfind(n)

    for _ in range(m):
        a,b=map(int,input().split())
        if uf.same(a-1,b-1) : continue
        uf.unite(a-1,b-1)

    print(max(uf.num))

main()



class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


def __starting_point():
    n, m = list(map(int, input().split()))
    uf = UnionFind(n)

    for _ in range(m):
        a, b = list(map(int, input().split()))
        uf.union(a-1, b-1)

    max_firiend = 0

    uf_root = uf.roots()
    for r in uf_root:
        max_firiend = max(max_firiend, uf.size(r))

    print(max_firiend)

__starting_point()

class UnionFind():
    def __init__(self, n):
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

n, m = map(int, input().split())

uf = UnionFind(n)

for _ in range(m):
    a, b = map(int, input().split())
    a, b = a-1, b-1
    uf.union(a, b)

print(min(uf.parents)*-1)

class UnionFind(): 
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]


N,M=list(map(int,input().split()))

uf=UnionFind(N)

for i in range(M):
    A,B=list(map(int,input().split()))
    a=uf.find(A-1)
    b=uf.find(B-1)
    uf.union(a,b)

ans=0

for i in uf.roots():
    ans=max(uf.size(i),ans)

print(ans)    





        



















    

                
            





    









        

    









    

        




    















    



    





    






        






    
















        


























            
    


    


    
    














    


    














N, M = list(map(int, input().split()))
AB = [list(map(int, input().split())) for _ in range(M)]


class UnionFind(object):
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]


uf = UnionFind(N)
for a, b in AB:
    uf.union(a - 1, b - 1)

max_ = 0
roots = uf.roots()

for r in roots:
    max_ = max(max_, uf.size(r))

print(max_)


n,m=map(int,input().split())
class UnionFind():
    def __init__(self,n):
        self.n=n
        self.root=[-1]*(n+1)
        self.rank=[0]*(n+1)
      
    def find(self,x):
        if self.root[x]<0:
            return x
        else:
            self.root[x]=self.find(self.root[x])
            return self.root[x]
    
    def unite(self,x,y):
        x=self.find(x)
        y=self.find(y)
        if x==y:
            return 0
        elif self.rank[x]>self.rank[y]:
            self.root[x]+=self.root[y]
            self.root[y]=x
        else:
            self.root[y]+=self.root[x]
            self.root[x]=y
        if self.rank[x]==self.rank[y]:
            self.rank[y]+=1
          
    def isSame(self,x,y):
        return self.find(x)==self.find(y)
    
    def size(self,x):
        return -self.root[self.find(x)]
link=UnionFind(n)
for _ in range(m):
  a,b=map(int,input().split())
  a-=1
  b-=1
  link.unite(a,b)
print(-min(link.root))

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
      
      
N, M = list(map(int,input().split()))
uf = UnionFind(N)
for i in range(M):
  A, B = list(map(int,input().split()))
  A -= 1
  B -= 1
  uf.union(A, B)
  
ans = 0
for i in range(N):
  ans = max(ans, uf.size(i))
  
print(ans)
  


def root(a):
	if p[a] == a:
		return a
	p[a] = root(p[a])
	return p[a]

def merge(a, b, p, size):
	ra = root(a)
	rb = root(b)
	if ra == rb:
		return
	size[rb] += size[ra]
	p[ra] = rb

n,m = list(map(int, input().split()))

size = [1]*n
p = [0]*n
for i in range(n):
	p[i] = i

for i in range(m):
	a,b = list(map(int, input().split()))
	a -= 1
	b -= 1
	merge(a,b,p,size)

ans = 0
for x in size:
	ans = max(ans, x)

print(ans)


N, M = (int(d) for d in input().split())

C = [set() for i in range(N+1)]
for i in range(M):
    A, B = (int(d) for d in input().split())
    C[A].update([B])
    C[B].update([A])

# BFS
seen = set()
G = []

for i in range(1, N+1):
    if i in seen:
        continue

    # create new group
    G.append(set())

    # create queue
    Q = [i]
    G[-1].update(Q)
    seen.update(Q)

    while len(Q) > 0:
        # queue is not empty

        I = Q.pop(0)
        J = C[I].difference(seen)
        Q += list(J)
        G[-1].update(J)

        seen.update(J)

print((max([len(g) for g in G])))



class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x
    def size(self, x):return -self.parents[self.find(x)]
    def same(self, x, y):return self.find(x) == self.find(y)
    def members(self, x):root = self.find(x);return [i for i in range(self.n) if self.find(i) == root]
    def roots(self):return [i for i, x in enumerate(self.parents) if x < 0]
    def group_count(self):return len(self.roots())
    def all_group_members(self):return {r: self.members(r) for r in self.roots()}
    def __str__(self):return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
    def get_group(self):return [self.find(i)for i in range(self.n)]

N,M=map(int,input().split())
uf=UnionFind(N)
for i in range(M):
    A,B=map(int,input().split())
    uf.union(A-1,B-1)
print(max([uf.size(i)for i in range(N)]))

# -*- coding utf-8 -*-

MOD = 10 ** 9 + 7

N, M = list(map(int, input().split()))
AB_M = [list(map(int, input().split())) for _ in range(M)]

# union find
parents = [-1] * (N + 1)


def find(x):
    if parents[x] < 0:
        return x
    else:
        parents[x] = find(parents[x])
        return parents[x]


def union(x, y):
    x = find(x)
    y = find(y)

    if x == y:
        return

    if parents[x] > parents[y]:
        x, y = y, x

    parents[x] += parents[y]
    parents[y] = x


for ab in AB_M:
    union(ab[0], ab[1])

ans = - min(parents)

print(ans)


class UnionFind():
  def __init__(self, n):
    self.n=n
    self.parents=[-1]*n

  def find(self,x):
    if self.parents[x]<0:
      return x
    else:
      self.parents[x]=self.find(self.parents[x])
      return self.parents[x]

  def unite(self, x, y):
    x = self.find(x)
    y = self.find(y)
    if x == y:
      return
    if self.parents[x]>self.parents[y]:
      x,y=y,x
    self.parents[x]+=self.parents[y]
    self.parents[y]=x

  def size(self, x):
    return -self.parents[self.find(x)]

  def same(self, x, y):
    return self.find(x) == self.find(y)

  def roots(self):
    return [i for i,x in enumerate(self.parents) if x<0]

N,M=map(int,input().split())
uf=UnionFind(N)
for _ in range(M):
  a,b=map(int,input().split())
  uf.unite(a-1,b-1)
ans=0
for r in uf.roots():
  s=uf.size(r)
  ans=max(ans,s)
print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

n, m = map(int, input().split())
u = UnionFind(n)
for _ in range(m):
    a, b = map(int, input().split())
    u.union(a - 1, b - 1)
print(max(u.size(i) for i in range(n)))

import statistics
N,M=map(int,input().split())
A=[0]*M;B=[0]*M
par=[-1]*N

def find(x):
    if par[x] < 0:
        return x
    else:
        tank = []
        while par[x] >= 0:
            tank.append(x)
            x = par[x]
        for elt in tank:
            par[elt] = x
        return x
def unite(x,y):
    x = find(x)
    y = find(y)
    if x == y:
        return False
    else:
        par[x] += par[y]
        par[y] = x
        return True

for i in range(M):
  A[i],B[i]=sorted(map(int,input().split()))
  unite(A[i]-1,B[i]-1)

print(min(par)*-1)

N, M = list(map(int, input().split()))
A = [0] * M
B = [0] * M
R = [0] * N
for i in range(N):
    R[i] = set()

for i in range(M):
    A[i], B[i] = list(map(int, input().split()))
    R[A[i]-1].add(B[i]-1)
    R[B[i]-1].add(A[i]-1)

stack = set()
visited = set()
max_groups = 0

for i in range(N):
    if not i in visited:
        stack.add(i)
        groups = 0
        while True:
            current = stack.pop()
            visited.add(current)
            groups += 1
            stack |= (R[current] - visited)
            if not stack:
                break
        max_groups = max(max_groups,groups)

print(max_groups)

import sys

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


N, M = list(map(int, input().split()))

uf = UnionFind(N)

ans = 1
for i in range(M):
    a, b = list(map(int, input().split()))
    uf.union(a-1, b-1)
    ans = max(ans, uf.size(a-1))

print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * (n+1)

    def find(self, x):
        if self.parents[x] < 0:
            return x
        self.parents[x] = self.find(self.parents[x])
        return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -1 * self.parents[x]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i in range(1, self.n+1, 1) if self.parents[i] < 0]

    def group_count(self):
        return len(self.roots())


N, M = list(map(int, input().split()))
uf = UnionFind(N)
for i in range(M):
    a, b = list(map(int, input().split()))
    uf.union(a, b)
ans = 0
for j in uf.roots():
    ans = max(ans, uf.size(j))
print(ans)


import sys
from collections import deque, defaultdict, Counter
from itertools import accumulate, product, permutations, combinations
from operator import itemgetter
from bisect import bisect_left, bisect_right
from heapq import heappop, heappush
from math import ceil, floor, sqrt, gcd, inf
from copy import deepcopy
import numpy as np
import scipy as sp

INF = inf
MOD = 1000000007

n, m = [int(i) for i in input().split()]
A = [[int(i) for i in input().split()]for j in range(m)]    # nは行数

tmp = 0
res = 0

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1 for i in range(n)]

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.parents[x] > self.parents[y]:
                x, y = y, x
            self.parents[x] += self.parents[y]
            self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    @property
    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    @property
    def group_count(self):
        return len(self.roots)

    @property
    def all_group_members(self):
        return {r: self.members(r) for r in self.roots}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots)

uf = UnionFind(n)
for i in range(m):
    x, y = A[i]
    uf.union(x - 1, y - 1)
for i in uf.roots:
    res = max(res, uf.size(i))

print(res)


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
def main():
    N, M = map(int, input().split())
    uf = UnionFind(N)
    for _ in range(M):
        a,b = map(int,input().split())
        a -= 1
        b -= 1
        uf.union(a,b)
    ans = 0
    for i in range(N):
      ans = max(ans,uf.size(i))
    print(ans)
main()

from networkx.utils import UnionFind as UF
n,m=map(int, input().split())
uf=UF()
for _ in range(m):
    a,b=map(int, input().split())
    uf.union(a, b)
ans=1
for i in uf.to_sets():
    ans=max(ans,len(i))
print(ans)

import collections
import fractions
import itertools
import functools
import bisect
import heapq
import math
import operator
import sys

sys.setrecursionlimit(10**9+7)

N, M = list(map(int, input().split()))

class UnionFind():
    def __init__(self, n):
        self.parents = [-1] * n
    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x
    def size(self, n):
        return -self.parents[self.find(n)]

def solve():
    group = UnionFind(N)
    for i in range(M):
        a, b = [int(x)-1 for x in input().split()]
        group.union(a, b)
    ans = 0
    for i in range(N):
        ans = max(ans, group.size(i))
    print(ans)
    return 0

def __starting_point():
    solve()

__starting_point()

#!/usr/bin/env python3
class UnionFind:
    # 作りたい要素数nで初期化
    # 使用するインスタンス変数の初期化
    def __init__(self, n):
        self.n = n
        # root[x]<0ならそのノードが根かつその値が木の要素数
        # rootノードでその木の要素数を記録する
        self.root = [-1] * (n + 1)
        # 木をくっつける時にアンバランスにならないように調整する
        self.rnk = [0] * (n + 1)

    # ノードxのrootノードを見つける
    def Find_Root(self, x):
        if self.root[x] < 0:
            return x
        else:
            # ここで代入しておくことで、後の繰り返しを避ける
            self.root[x] = self.Find_Root(self.root[x])
            return self.root[x]

    # 木の併合、入力は併合したい各ノード
    def Unite(self, x, y):
        # 入力ノードのrootノードを見つける
        x = self.Find_Root(x)
        y = self.Find_Root(y)
        # すでに同じ木に属していた場合
        if x == y:
            return
        # 違う木に属していた場合rnkを見てくっつける方を決める
        elif self.rnk[x] > self.rnk[y]:
            self.root[x] += self.root[y]
            self.root[y] = x

        else:
            self.root[y] += self.root[x]
            self.root[x] = y
            # rnkが同じ（深さに差がない場合）は1増やす
            if self.rnk[x] == self.rnk[y]:
                self.rnk[y] += 1

    # xとyが同じグループに属するか判断
    def isSameGroup(self, x, y):
        return self.Find_Root(x) == self.Find_Root(y)

    # ノードxが属する木のサイズを返す
    def Count(self, x):
        return -self.root[self.Find_Root(x)]


def main():
    n, m = list(map(int, input().split()))
    uf = UnionFind(n)

    for i in range(m):
        a, b = list(map(int, input().split()))
        uf.Unite(a, b)

    for i in range(n):
        i += 1
        uf.isSameGroup(i, i)

    ans = 0
    for i in range(n):
        ans = max(ans, -uf.root[i + 1])

    # print(uf.root)
    print(ans)


main()


from collections import deque
n, m = list(map(int, input().split()))
# 隣接行列は作っておく
g = [[] for i in [0]*(n+1)]
for _ in range(m):
    a, b = list(map(int, input().split()))
    g[a].append(b)
    g[b].append(a)

visited = [0 for i in [0]*(n+1)]
ans = 1
for i in range(n+1):
  if visited[i]:
    continue
  Q = deque([i])
  group_friend = 1
  while Q:
    q = Q.popleft()
    visited[q] = 1
    link = g[q]
    for j in link:
      if visited[j]:
          continue
      visited[j] = 1
      group_friend += 1
      Q.append(j)
  ans = max(ans, group_friend)
print(ans)


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

    
N, M = map(int, input().split())

A = [0 for _ in range(M)]
B = [0 for _ in range(M)]

for i in range(M):
    A[i], B[i] = map(int,input().split())

uf = UnionFind(N)
for i in range(M):
    uf.union(A[i]-1, B[i]-1)

ans = 0
for i in range(uf.group_count()):
    ans = max(ans, uf.size(i))

print(ans)

import math
import string
import itertools
import fractions
import heapq
import collections
import re
import array
import bisect
import sys
import random
import time
inf = 10**9


class UnionFind():

    def __init__(self, n: int):
        """
        自身が親であれば、その集合に属する頂点数に-1を掛けたもの
        そうでなければ親のid
        """
        self.r = [-1]*n

    def root(self, x: int):
        if self.r[x] < 0:
            return x
        self.r[x] = self.root(self.r[x])
        return self.r[x]

    def unite(self, x: int, y: int):
        x, y = self.root(x), self.root(y)
        if x == y:
            return
        if self.r[x] > self.r[y]:
            x, y = y, x
        self.r[x] += self.r[y]
        self.r[y] = x

    def same(self, x, y):
        return self.root(x) == self.root(y)


def main():
    n, m = list(map(int, input().split()))
    uf = UnionFind(n)
    for i in range(m):
        a, b = list(map(int, input().split()))
        uf.unite(a-1, b-1)
    print((-min(uf.r)))


def __starting_point():
    main()

__starting_point()

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x) -> int:
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y) -> None:
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x) -> int:
        return -self.parents[self.find(x)]

    def same(self, x, y) -> bool:
        return self.find(x) == self.find(y)

    def members(self, x) -> list:
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self) -> list:
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self) -> int:
        return len(self.roots())

    def all_group_members(self) -> dict:
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

    def read(self,m):
        for i in range(m):
            a,b=list(map(int,input().split()))
            self.union(a-1,b-1)

    def solve(self):
        print((max([self.size(i) for i in self.roots()])))

n,m=list(map(int,input().split()))
a=UnionFind(n)
a.read(m)
a.solve()


import networkx as nx
n,m=map(int, input().split())
uf=nx.utils.UnionFind()
for _ in range(m):
    a,b=map(int, input().split())
    uf.union(a, b)
ans=1
for i in uf.to_sets():
    ans=max(ans,len(i))
print(ans)

def find(target):
    if parent[target] < 0:
        return target
    else:
        parent[target] = find(parent[target])
        return parent[target]

def is_same(x, y):
    return find(x) == find(y)

def union(x, y):
    root_x = find(x)
    root_y = find(y)
    if root_x == root_y:
        return
    if parent[root_x] > parent[root_y]:
        root_x, root_y = root_y, root_x
    parent[root_x] += parent[root_y]
    parent[root_y] = root_x

# 今回これ使わないけど、どこに誰がいるのかはこれでわかる
def members(n, x):
    root = find(x)
    return [i for i in range(n) if find(i) == root]

def get_size(x):
    return -parent[find(x)]

def get_root():
    return [i for i, root in enumerate(parent) if root < 0]
n, m = map(int, input().split())
parent = [-1 for _ in range(n)]
for _ in range(m):
    a, b = map(lambda x: int(x) - 1, input().split())
    union(a, b)
ans = 0
for i in range(n):
    ans = max(ans, get_size(i))
print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
        self.size=[1]*n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.size[x]+=self.size[y]
        self.size[y]=self.size[x]
        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

n,m=list(map(int,input().split()))
uf = UnionFind(n)
for i in range(m):
    a,b=list(map(int,input().split()))
    uf.union(a-1,b-1)
print((max(uf.size)))


# Union-Find
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [i for i in range(n + 1)]

    def find(self, x):
        if self.parents[x] == x:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return 0
        elif x < y:
            self.parents[y] = x
        else:
            self.parents[x] = y
        return 1

from collections import Counter
n, m = list(map(int, input().split()))
uf = UnionFind(n)
for _ in range(m):
    a, b = list(map(int, input().split()))
    uf.unite(a, b)
roots = []
for num in uf.parents:
    roots.append(uf.find(num))
count_roots = Counter(roots)
print((max(count_roots.values())))


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

n,m=map(int,input().split())
uf=UnionFind(n)
for i in range(m):
  a,b=map(int,input().split())
  uf.union(a-1,b-1)
ans=0
for i in range(n):
  ans=max(ans,uf.size(i))
print(ans)

import statistics
N,M=list(map(int,input().split()))
A=[0]*M;B=[0]*M
par=[-1]*N

def find(x):
    if par[x] < 0:
        return x
    else:
        tank = []
        while par[x] >= 0:
            tank.append(x)
            x = par[x]
        for elt in tank:
            par[elt] = x
        return x
def unite(x,y):
    x = find(x)
    y = find(y)
    if x == y:
        return False
    else:
        par[x] += par[y]
        par[y] = x
        return True

for i in range(M):
  A[i],B[i]=sorted(map(int,input().split()))
  unite(A[i]-1,B[i]-1)

print((min(par)*-1))


import sys
sys.setrecursionlimit(1000000)

f=lambda:map(int,input().split())
N,M=map(int,input().split())

# par=[i for i in range(N+1)]
par=[-1]*(N+1)

def find(x):
    if par[x]<0:return(x)
    else:
        par[x]=find(par[x])
        return(par[x])

def unite(x,y):
    #print("unite1",x,y)
    x=find(x)
    y=find(y)
    #print("unite2",x,y)
    if x==y:return # rootが同じなのでマージは必要ない
    if (par[x]>par[y]):x,y=y,x
    par[x]+=par[y]
    par[y]=x
    #print(par)
    return
    
for i in range(M):
    a,b=map(int,input().split())
    unite(a,b)

print(-min(par[1:]))

from collections import deque
n, m = map(int, input().split())
E = [[] for _ in range(n)]
for i in range(m):
    a, b = map(int, input().split())
    a -= 1
    b -= 1
    E[a].append(b)
    E[b].append(a)
flag = [True for _ in range(n)]
ans = 0
for i in range(n):
    if flag[i]:
        counter = 1
        flag[i] = False
        que = deque([i])
        while que:
            now = deque.popleft(que)
            for j in E[now]:
                if flag[j]:
                    deque.append(que, j)
                    counter += 1
                    flag[j] = False
        ans = max(ans, counter)
print(ans)

# Union Find

# xの根を求める
def find(x):
    if par[x] < 0:
        return x
    else:
        tank = []
        while par[x] >= 0:
            tank.append(x)
            x = par[x]
        for elt in tank:
            par[elt] = x
        return x


# xとyの属する集合の併合
def unite(x, y):
    x = find(x)
    y = find(y)

    if x == y:
        return False
    else:
        # sizeの大きいほうがx
        if par[x] > par[y]:
            x, y = y, x
        par[x] += par[y]
        par[y] = x
        return True


# xとyが同じ集合に属するかの判定
def same(x, y):
    return find(x) == find(y)


# xが属する集合の個数
def size(x):
    return -par[find(x)]


# 初期化
# 根なら-size,子なら親の頂点
n,m = map(int,input().split())
par = [-1] * n
ans = 0
for i in range(m):
    X,Y = map(int,input().split())
    unite(X-1,Y-1)

for i in range(n):
    ans = max(size(i),ans)
print(ans)

# -*- coding: utf-8 -*-

# 入力を整数に変換して受け取る
def input_int():
    return int(input())


# マイナス1した値を返却
def int1(x):
    return int(x) - 1


# 半角スペース区切り入力をIntに変換してMapで受け取る
def input_to_int_map():
    return list(map(int, input().split()))


# 半角スペース区切り入力をIntに変換して受け取る
def input_to_int_tuple():
    return tuple(map(int, input().split()))


# 半角スペース区切り入力をIntに変換してマイナス1した値を受け取る
def input_to_int_tuple_minus1():
    return tuple(map(int1, input().split()))


def main():
    N, M = input_to_int_map()
    AB = (input_to_int_tuple() for i in range(M))

    from collections import defaultdict
    friends = defaultdict(lambda :-1)

    def root(x):
        temp = friends[x]
        if temp < 0:
            return x
        temp2 = root(temp)
        friends[x] = temp2
        return temp2

    for a, b in AB:
        a = root(a)
        b = root(b)
        if a == b:
            continue

        if (friends[a] > friends[b]):
            temp = a
            a = b
            b = temp
        friends[a] += friends[b]
        friends[b] = a

    ret = 0
    for i in range(1, N + 1):
        temp = root(i)
        ret = max(ret, -friends[temp])

    print(ret)


def __starting_point():
    main()

__starting_point()

class union_find():

    def __init__(self, n):
        self.n = n
        self.root = [-1]*(n+1)
        self.rank = [0]*(n+1)

    def find_root(self, x):
        if self.root[x] < 0:
            return x
        else:
            self.root[x] = self.find_root(self.root[x])
            return self.root[x]

    def unite(self, x, y):
        x = self.find_root(x)
        y = self.find_root(y)
        if x == y:
            return
        elif self.rank[x] > self.rank[y]:
            self.root[x] += self.root[y]
            self.root[y] = x
        else:
            self.root[y] += self.root[x]
            self.root[x] = y
            if self.rank[x] == self.rank[y]:
                self.rank[y] += 1

    def same(self, x, y):
        return self.find_root(x) == self.find_root(y)

    def cnt(self, x):
        return -self.root[self.find_root(x)]


n, m = map(int, input().split())
g = union_find(n)
ans = 0
for _ in range(m):
    a, b = map(int, input().split())
    g.unite(a, b)

for i in range(1, n+1):
    ans = max(ans, g.cnt(i))

print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

N, M = map(int,input().split())

ans = 0
#UnionFind木を作る
uf = UnionFind(N)

for _ in range(M):
    a, b = map(int,input().split())
    uf.union(a-1, b-1)

parents = uf.roots()

for p in parents:
    ans = max(ans,uf.size(p))

print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

n,m = map(int,input().split())
uf = UnionFind(n)
for i in range(m):
    a, b = map(int, input().split())
    uf.union(a-1, b-1)
box = []
for i in range(n):
    box.append(uf.size(i))
print(max(box))

class UnionFind():  # 0インデックス
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x


    def size(self, x):
        return -self.parents[self.find(x)]


def __starting_point():
    n, m = list(map(int, input().split()))
    unionFind = UnionFind(n)
    for _ in range(m):
        a, b = list(map(int, input().split()))
        a -= 1
        b -= 1
        unionFind.union(a, b)

    value = 0
    for i in range(n):
        value = max(value, unionFind.size(i))

    print(value)


__starting_point()

#from statistics import median
#import collections
#aa = collections.Counter(a) # list to list || .most_common(2)で最大の2個とりだせるお a[0][0]
from math import gcd
from itertools import combinations,permutations,accumulate, product # (string,3) 3回
#from collections import deque
from collections import deque,defaultdict,Counter
import decimal
import re
import math
import bisect
import heapq
#
#
#
# pythonで無理なときは、pypyでやると正解するかも！！
#
#
# my_round_int = lambda x:np.round((x*2 + 1)//2)
# 四捨五入g
#
# インデックス系
# int min_y = max(0, i - 2), max_y = min(h - 1, i + 2);
# int min_x = max(0, j - 2), max_x = min(w - 1, j + 2);
#
#
import sys
sys.setrecursionlimit(10000000)
mod = 10**9 + 7
# mod = 9982443453
# mod = 998244353
INF = float('inf')
from sys import stdin
readline = stdin.readline
def readInts():
  return list(map(int,readline().split()))
def readTuples():
    return tuple(map(int,readline().split()))
def I():
    return int(readline())
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x
    def size(self, x):
        return -self.parents[self.find(x)]
n,m = readInts()
uni = UnionFind(n)
for _ in range(m):
    a,b = readInts()
    a-=1
    b-=1
    uni.union(a,b)
ans = -1
for i in range(n):
    ans = max(ans, uni.size(i))
print(ans)


import collections
import fractions
import itertools
import functools
import bisect
import heapq
import math
import operator
import sys

sys.setrecursionlimit(10**9+7)

N, M = list(map(int, input().split()))

class UnionFind():
    def __init__(self, n):
        self.parents = [-1] * n
    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x
    def size(self, n):
        return -self.parents[self.find(n)]

def solve():
    group = UnionFind(N)
    for i in range(M):
        a, b = [int(x)-1 for x in input().split()]
        group.union(a, b)
    ans = 0
    for i in range(N):
        ans = max(ans, group.size(i))
    print(ans)
    return 0

def __starting_point():
    solve()


__starting_point()

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
 
    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
 
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
 
        if x == y:
            return
 
        if self.parents[x] > self.parents[y]:
            x, y = y, x
 
        self.parents[x] += self.parents[y]
        self.parents[y] = x
 
    def size(self, x):
        return -self.parents[self.find(x)]
 
    def same(self, x, y):
        return self.find(x) == self.find(y)
 
    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]
 
    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]
 
    def group_count(self):
        return len(self.roots())
 
    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}
 
    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
n,m=map(int,input().split())
U=UnionFind(n)
for i in range(m):
  a,b=map(int,input().split())
  a,b=a-1,b-1
  U.union(a,b)
print(-min([i for i in U.parents]))

n, m = list(map(int, input().split()))
# friend array
f = [i for i in range(n)]

# 友だち集合の中で最小IDを返す
def find(x):
    if f[x] == x:
        return x
    ret = find(f[x])
    f[x] = ret
    return ret

# 友だち集合を作る
def union(x, y):
    i, j = find(x), find(y)
    f[max(i, j)] = min(i, j)

for _ in range(m):
    a, b = list(map(int, input().split()))
    a, b = a-1, b-1
    union(a, b)

cnt = [0 for _ in range(n)]
for i in range(n):
    cnt[find(i)] += 1
print((max(cnt)))


class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
 
    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
 
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
 
        if x == y:
            return
 
        if self.parents[x] > self.parents[y]:
            x, y = y, x
 
        self.parents[x] += self.parents[y]
        self.parents[y] = x
 
    def size(self, x):
        return -self.parents[self.find(x)]
 
    def same(self, x, y):
        return self.find(x) == self.find(y)
 
    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]
 
    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]
 
    def group_count(self):
        return len(self.roots())
 
    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}
 
    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


def __starting_point():
  n,m=map(int,input().split())
  uf=UnionFind(n)

  for i in range(m):
    a,b=map(int,input().split())
    uf.union(a-1,b-1)
  
  ans=0
  for i in range(n):
    ans=max(ans,uf.size(i))
  print(ans)
__starting_point()

N, M = map(int, input().split())
 
 
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
 
    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
 
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
 
        if x == y:
            return
 
        if self.parents[x] > self.parents[y]:
            x, y = y, x
 
        self.parents[x] += self.parents[y]
        self.parents[y] = x
 
    def size(self, x):
        return -self.parents[self.find(x)]
 
    def same(self, x, y):
        return self.find(x) == self.find(y)
 
    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]
 
    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]
 
    def group_count(self):
        return len(self.roots())
 
    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}
 
    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
 
 
u = UnionFind(N)
for _ in range(M):
    a, b = map(int, input().split())
    u.union(a - 1, b - 1)
 
ans = 0
for i in u.roots():
    ans = max(u.size(i), ans)
 
print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


n,m = list(map(int, input().split()))
uf = UnionFind(n)

for i in range(m):
    a,b = list(map(int, input().split()))
    uf.union(a-1,b-1)

ans = 0
for i in range(n):
    ans = max(ans, uf.size(i))

print(ans)


import statistics
N,M=map(int,input().split())
A=[0]*M;B=[0]*M
par=[-1]*N

def find(x):
    if par[x] < 0:
        return x
    else:
        tank = []
        while par[x] >= 0:
            tank.append(x)
            x = par[x]
        for elt in tank:
            par[elt] = x
        return x
def unite(x,y):
    x = find(x)
    y = find(y)
    if x == y:
        return False
    else:
        par[x] += par[y]
        par[y] = x
        return True

for i in range(M):
  A[i],B[i]=sorted(map(int,input().split()))
  unite(A[i]-1,B[i]-1)

for i in range(N):
  par[i]*=-1

#print(par)
print(max(par))

#Union-Find
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return 

        if self.parents[x] > self.parents[y]:
            x,y = y,x
        
        self.parents[x] += self.parents[y]
        self.parents[y] = x
    
    def size(self, x):
        return -self.parents[self.find(x)]


def __starting_point():
    n,m = map(int,input().split())
    unionfind = UnionFind(n)
    for _ in range(m):
        a,b = map(int,input().split())
        unionfind.union(a-1, b-1)
        
    answer = []
    for i in range(n):
        answer.append(unionfind.size(i))

    print(max(answer))
__starting_point()

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

n, m = map(int, input().split())
a = [list(map(int, input().split())) for i in range(m)]
uf = UnionFind(n)
ans = 0
for i in range(m):
    uf.union(a[i][0]-1, a[i][1]-1)
for i in range(n):
    ans = max(ans, uf.size(i))
print(ans)

import networkx as nx
n,m=map(int, input().split())
uf=nx.utils.UnionFind()
for _ in range(m):
    a,b=map(int, input().split())
    uf.union(a, b)
ans=1
for i in uf.to_sets():
    ans=max(ans,len(i))
print(ans)

#クラスカル法
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x): #親を返す
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def unite(self, x, y): #和集合の生成
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x): #所属する集合の大きさ
        return -self.parents[self.find(x)]

    def same(self, x, y): #同じ集合に属しているか判定
        return self.find(x) == self.find(y)

    def members(self, x): #同じ集合に属する要素全列挙
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self): #集合のリーダー全列挙
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self): #集合の数
        return len(self.roots())

    def all_group_members(self): #辞書型,{(リーダーの番号):(その集合の要素全列挙)}
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

n,m=map(int,input().split())
uf=UnionFind(n)
for i in range(m):
    a,b=map(int,input().split())
    uf.unite(a-1,b-1)
print(-min(uf.parents))

class UnionFind:
    def __init__(self, n):
        self.n = n
        self.parent = [i for i in range(n)]  # 親
        self.rank = [1] * n  # 木の高さ
        self.size = [1] * n  # size[i] は i を根とするグループのサイズ

    def find(self, x):  # x の根を返す
        if self.parent[x] == x:
            return x
        else:
            self.parent[x] = self.find(self.parent[x])  # 経路圧縮
            return self.parent[x]

    def unite(self, x, y):  # x, y の属する集合を併合する
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                self.parent[x] = y
                self.size[y] += self.size[x]
            else:
                self.parent[y] = x
                self.size[x] += self.size[y]
                if self.rank[x] == self.rank[y]:
                    self.rank[x] += 1

    def is_same(self, x, y):  # x, y が同じ集合に属するか判定する
        return self.find(x) == self.find(y)

    def group_size(self, x):  # x が属する集合の大きさを返す
        return self.size[self.find(x)]

    def group_members(self, x):  # x が属する集合の要素を返す
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):  # すべての根をリストで返す
        return [i for i, x in enumerate(self.parent) if i == x]

    def group_count(self):  # 木の数を返す
        return len(self.roots())

    def all_group_members(self):  # すべての木の要素を辞書で返す
        return {r: self.group_members(r) for r in self.roots()}

    def __str__(self):  # print 表示用
        return '\n'.join('{}: {}'.format(r, self.group_members(r)) for r in self.roots())


n, m = map(int, input().split())
uf = UnionFind(n+1)

for _ in range(m):
    a, b = map(int, input().split())
    uf.unite(a, b)

print(max(uf.size))

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())
      
N,M = map(int,input().split())
uf = UnionFind(N)
for i in range(M):
  a,b = map(int,input().split())
  uf.union(a-1,b-1)
max = 0
for i in range(N):
  if max < uf.size(i):
    max = uf.size(i)
print(max)

from collections import Counter
import math
import statistics
import itertools
# H,W,K=map(int,input().split())
#b=int(input())
# c=[]
# for i in a:
#     c.append(int(i))
# f = list(map(int,input().split()))
#g = [list(map(lambda x: '{}'.format(x), list(input()))) for _ in range(a)]
# h = []
# for i in range(a):
#     h.append(list(map(int,input().split())))
#two = [list(input()) for _ in range(H)]#nizigen
# lis=[]
# for i in range(Q):
#     lis.append(list(map(int,input().split())))
# a=list(map(int,input().split()))
N,M = list(map(int,input().split()))
par = [i for i in range(1,N+1)]
rank=[0]*N
# rank = [0]*(N)
def find(x):
    if par[x-1] == x:#自分が親であるかどうか
        return x
    else:
        par[x-1] = find(par[x-1]) #経路圧縮
        return par[x-1]
def same(x,y):
    return find(x) == find(y)
def unite(x,y):
    x = find(x)
    y = find(y)
    if x == y:
        return 0
    if rank[x-1] < rank[y-1]:
        par[x-1] = y
    else:
        par[y-1] = x
        if rank[x-1]==rank[y-1]:rank[x-1]+=1

for i in range(M):
    a,b = list(map(int,input().split()))
    unite(a,b)

cnt=[0]*N
for i in range(N):
    cnt[find(i)-1]+=1
print((max(cnt)))



def A():
    d, t, s = list(map(int, input().split()))
    print(("Yes" if t*s>=d else "No"))

def B():
    s = input()
    t = input()
    sa = len(s) - len(t)
    ret = 10000000000
    for i in range(sa+1):
        cnt = 0
        for j in range(len(t)):
            if s[j+i] != t[j]: cnt += 1
        ret = min(ret, cnt)
    print(ret)

def C():
    int(input())
    A = list(map(int, input().split()))
    S = sum(A)
    T = 0
    for e in A:
        T += e*e
    print(((S * S - T)//2))

class UF:
    def __init__(self, N):
        self.N = N
        self.sz = [1] * N
        self.par = [i for i in range(N)]
        self.d = [1] * N

    def find(self, x):
        if self.par[x] == x: return x
        self.par[x] = self.find(self.par[x])
        return self.par[x]

    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y: return False
        if self.d[y] > self.d[x]:
            self.par[x] = y
            self.sz[y] += self.sz[x]
        else:
            self.par[y] = x
            self.sz[x] += self.sz[y]
            if self.d[x] == self.d[y]: self.d[x] += 1

def D():
    n, m = list(map(int, input().split()))
    uf = UF(n+1)
    for i in range(m):
        a, b = list(map(int, input().split()))
        uf.unite(a, b)
    print((max(uf.sz)))

D()


from sys import stdin
nii=lambda:map(int,stdin.readline().split())
lnii=lambda:list(map(int,stdin.readline().split()))

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def size(self, x):
        return -self.parents[self.find(x)]

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

n,m=nii()
uf=UnionFind(n)
for i in range(m):
  a,b=nii()
  a-=1
  b-=1
  uf.union(a,b)

root=uf.roots()
ans=0
for i in root:
  ans=max(ans,uf.size(i))

print(ans)

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

n,m = map(int,input().split())
abl = [list(map(int,input().split())) for nesya in range(m)]
uf = UnionFind(n)
for ab in abl:
  uf.union(ab[0]-1,ab[1]-1)
ans = 0
for hoge in uf.roots():
  ans = max(ans,uf.size(hoge))
print(ans)

import sys
sys.setrecursionlimit(10**9)

n, m = map(int, input().split())
pair = [-1]*n

def find(x):
    if pair[x] < 0:
        return x
    else:
        pair[x] = find(pair[x])
        return pair[x]

def unite(x, y):
    x = find(x)
    y = find(y)

    if x == y:
        return

    pair[x] += pair[y]
    pair[y] = x

def size(x):
    x = find(x)
    return -pair[x]

for _ in range(m):
    x, y = map(int, input().split())
    x -= 1
    y -= 1
    unite(x, y)

max_size = max(map(size, range(n)))
print(max_size)