CodeEff/ECCO huggingface dataset:
# Original solution
import sys
input=sys.stdin.readline
sys.setrecursionlimit(10 ** 8)
from itertools import accumulate
from itertools import permutations
from itertools import combinations
from collections import defaultdict
from collections import Counter
import fractions
import math
from collections import deque
from bisect import bisect_left
from bisect import bisect_right
from bisect import insort_left
import itertools
from heapq import heapify
from heapq import heappop
from heapq import heappush
import heapq
from copy import deepcopy
from decimal import Decimal
alf = list("abcdefghijklmnopqrstuvwxyz")
ALF = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
#import numpy as np
INF = float("inf")
#d = defaultdict(int)
#d = defaultdict(list)
N,M = list(map(int,input().split()))
count = [[0]*N for _ in range(N)]
fr = [[] for _ in range(N)]
for _ in range(M):
    a,b = list(map(int,input().split()))
    a,b = a-1,b-1
    if count[a][b] == 1:
        continue
    count[a][b] = 1
    count[b][a] = 1
    fr[a].append(b)
    fr[b].append(a)
used = [-1]*N 
MAX = 0
def dfs(fr,cur,parent,cnt):
    global MAX
    MAX = max(cnt,MAX)
    children = fr[cur]
    for chi in children:
        if chi == parent or used[chi] != -1:
            continue
        used[chi] = 0
        cnt += 1
        dfs(fr,chi,cur,cnt)
    return

for i in range(N):
    cnt = 1
    if used[i] != -1:
        continue
    used[i] = 0
    dfs(fr,i,-1,cnt)
print(MAX)

            








# Optimized solution
import sys
sys.setrecursionlimit(99999999)


[n,m]=list(map(int,input().split()))
friends=[]
for i in range(m):
    friends.append(list(map(int,input().split())))

tomodachi=[[]for i in range(n)]

for i in range(m):
    tomodachi[friends[i][0]-1].append(friends[i][1]-1)
    tomodachi[friends[i][1]-1].append(friends[i][0]-1)

for i in range(n):
    tomodachi[i]=list(set(tomodachi[i]))

namelist=[1 for i in range(n)]
teams=[]

def haba(x,kari,tomodachi,namelist):
    for i in range(len(tomodachi[x])):
        if namelist[tomodachi[x][i]]==1:#生き残っているなら
            kari.append(tomodachi[x][i])
            namelist[tomodachi[x][i]]=0#殺す
            haba(tomodachi[x][i],kari,tomodachi,namelist)
    return(kari)
            


for i in range(n):
    if namelist[i]==1:
        teams.append(list(set(haba(i,[i],tomodachi,namelist))))

saidai=0

for i in range(len(teams)):
    if saidai<len(teams[i]):
        saidai=len(teams[i])

ans=saidai

print(ans)
# Original solution
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()))
  uf.union(a-1,b-1)

dic=uf.all_group_members()
max_size=0
for ss in list(dic.values()):
  max_size=max(max_size,len(ss))

print(max_size)

# Optimized solution
n,m=list(map(int,input().split()))
par = [-1]*(n+1)
def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x]) #経路圧縮
        return par[x]
def same(x,y):
    return find(x) == find(y)
def unite(x,y):
    x = find(x)
    y = find(y)
    if x == y:
      return 0
    else:
      if par[x] > par[y]:
        x,y = y,x
      par[x] += par[y]
      par[y] = x
def size(x):
    return -par[find(x)]

max_size=0
for _ in range(m):
  a,b=list(map(int,input().split()))
  unite(a,b)
  
print((-min(par)))
# Original solution
N,M=list(map(int,input().split()))

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

from collections import deque

over=[] #探索ずみ
before=[] #探索前
for i in range(N):
    before.append(i)

dist=[float('inf')]*N
cnt=0
cup=[]
while before:
    v=before.pop(0)
    que=deque([v])
    dist[v]=0
    cuppy=[]
    cuppy.append(v)
    while que:
        u=que.popleft()
        
        for e in G[u]:
            if dist[e] == float('inf'):
                
                que.append(e)
                before.remove(e)
                cuppy.append(e)
                dist[e]=dist[u] + 1
    
    cup.append(cuppy)
    cnt += 1


ans=0
for i in range(len(cup)):
    ans = max(ans,len(cup[i]))
print(ans)

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

class UnionFind():
    def __init__(self,n): #n:要素数
        self.n=n
        self.parents = [-1]*n 
        #parents:各要素の親要素番号を格納
        #要素が根である場合、-(そのグループの要素数)を格納する
    
    def find(self,x): #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のグループとyのグループを併合する
        x = self.find(x)
        y = self.find(y)
        
        if x==y:
            return
        
        if self.parents[x] > self.parents[y]: #|xのグループ|<|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)]

uf=UnionFind(N)
for i in range(M):
    a,b=[int(x)-1 for x in input().split()]
    uf.union(a,b)

ans=0

for i in range(N):
    if uf.size(i) > ans:
        ans=uf.size(i)
        
print(ans)
# Original solution
n, m = list(map(int,input().split()))

class unionfind():
    def __init__(self,n):
        self.li = [i for i in range(n+1)]
        self.group = [1]*(n+1)

    def find(self, x):
        while self.li[x]!=x:
            x = self.li[x]
        return x

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return;
        elif x>y:
            self.li[y] = x
            self.group[x] += self.group[y]
        else:
            self.li[x] = y
            self.group[y] += self.group[x]
x = unionfind(n)
ans = 0
for _ in range(m):
    a,b = list(map(int,input().split()))
    x.union(a,b)
print((max(x.group)))

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

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

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

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

x = unionfind(n)
ans = 0
for _ in range(m):
    a,b = list(map(int,input().split()))
    x.union(a,b)
print((-min(x.li)))
# Original solution
from collections import deque
import sys
input = sys.stdin.readline


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

table = [[] for i in range(N)]
for i in range(M):
    a,b = list(map(int,input().split()))
    a -= 1
    b -= 1
#    print(a,b)
    table[a].append(b)
    table[b].append(a)

visited = [0]*N
visited[0] = 1
H = deque()
H.append(0)
ans = 0
c = 1

while H:
    ans = max(ans,c)
    x = H.popleft()
#    print("x:",x,"c:",c)
#    print(visited)
    for y in table[x]:
        if visited[y] == 0:
            H.append(y)
            visited[y] = 1
#            print(y,"added")
            c += 1
    if not H:
        for i in range(N):
            if visited[i] == 0:
                H.append(i)
                visited[i] = 1
                c = 1
                break

print(ans)

# Optimized solution
class UnionFind:
    def __init__(self,N):
        self.parent = [i for i in range(N)]
        self.rank = [0] * N
        self.count = 0
        self._size = [1]*N
    def root(self,a):
        if self.parent[a] == a:
            return a
        else:
            self.parent[a] = self.root(self.parent[a])
            return self.parent[a]
    def is_sama(self,a,b):
        return self.root(a) == self.root(b)
    def unite(self,a,b):
        ra = self.root(a)
        rb = self.root(b)
        if ra == rb:
            return
        if self.rank[ra] < self.rank[rb]:
            self.parent[ra] = rb
            self._size[rb] += self._size[ra]
        else:
            self.parent[rb] = ra
            if self.rank[ra] == self.rank[rb]:
                self.rank[ra] += 1
            self._size[ra] += self._size[rb]
        self.count += 1
    def print_info(self):
        print(("parent", self.parent))
        print(("rank", self.rank))
        print(("count", self.count))
        print(("size", self._size))
    def size(self,a):
        return self._size[self.root(a)]

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.unite(a,b)
ans = 0
for i in range(N):
    ans = max(ans,uf.size(i))
print(ans)
# Original solution
from collections import deque

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

g = {i:set() for i in range(n+1)}

for i in range(m):
    a,b = list(map(int,input().split()))
    g[a].add(b)
    g[b].add(a)
    
seen = {i:0 for i in range(n+1)}
seen[0]=1
seen[1]=1
q = deque()
q.append(1)
grpsize=1

while 0 in list(seen.values()):
    if len(q)>0:
        now = q.popleft()
        nextp = g[now]
        for i in nextp:
            if seen[i]==0:
                q.append(i)
                grpsize+=1
                seen[i]=grpsize
        
    else:
        for i in range(1,n+1):
            if seen[i]==0:
                grpsize=1
                seen[i]=grpsize
                q.append(i)
                break
                
print((max(seen.values())))

# Optimized solution
from sys import setrecursionlimit

def find(par,i):
    if par[i]<0:
        return i
    par[i] = find(par,par[i])
    return par[i]

def unite(par,i,j):
    ri = find(par,i)
    rj = find(par, j)
    if ri==rj:
        return
    par[rj] += par[ri]
    par[ri] = rj
    
setrecursionlimit(10**6)
n,m = list(map(int,input().split()))

parent = [-1]*n
for _ in range(m):
    a,b = [int(x)-1 for x in input().split()]
    unite(parent,a,b)
    
print((-min(parent)))
# Original solution
n,m = list(map(int,input().split()))
par = []
for i in range(n):
    par.append(i)  #初期親
rank = [1 for i in range(n)] #初期ランク
cut = []
def find(n): #親検索andランク短縮
    global cut
    if par[n] == n:
        for i in range(len(cut)):
            par[cut[i]] = n
        cut = []
        return n
    else:
        cut.append(n)
        find(par[n])
        return find(par[n])
def shorten(n): # 根に直接接続する
    global cut
    if par[n] == n:
        for i in range(len(cut)):
            par[cut[i]] = n
        cut = []
    else:
        cut.append(n)
        shorten(par[n])
def unite(a,b): #グループ併合
    x = find(a)
    y = find(b) #根っこ同士をくっつける
    if x == y: 
        return    #既に同一ユニオンなら何もしない
    if rank[x] < rank[y]:
        par[x] = y 
    elif rank[x] == rank[y]:
        par[y] = x
        rank[x] += 1
    else:
        par[y] = x
def judge(a,b):
    return par[a] == par[b]
for i in range(m):
    a,b = list(map(int,input().split()))
    a -= 1
    b -= 1
    unite(a,b)
for i in range(n):
    shorten(i)
import collections
c = collections.Counter(par)
ans = 0
for i in range(n):
    ans = max(ans,c[i])
print(ans)

# Optimized solution
n,m = list(map(int,input().split()))
par = []
for i in range(n):
    par.append(-1)  #初期親
rank = [1 for i in range(n)] #初期ランク
cut = []
def find(n): #親検索andランク短縮
    global cut
    if par[n] < 0:
        for i in range(len(cut)):
            par[cut[i]] = n
        cut = []
        return n
    else:
        cut.append(n)
        find(par[n])
        return find(par[n])
def shorten(n): # 根に直接接続する
    global cut
    if par[n] < 0:
        for i in range(len(cut)):
            par[cut[i]] = n
        cut = []
    else:
        cut.append(n)
        shorten(par[n])
def unite(a,b): #グループ併合
    x = find(a)
    y = find(b) #根っこ同士をくっつける
    if x == y: 
        return    #既に同一ユニオンなら何もしない
    if rank[x] < rank[y]:
        par[y] += par[x]
        par[x] = y 
    elif rank[x] == rank[y]:
        par[x] += par[y]
        par[y] = x
        rank[x] += 1
    else:
        par[x] += par[y]
        par[y] = x
def judge(a,b):
    return par[a] == par[b]
for i in range(m):
    a,b = list(map(int,input().split()))
    a -= 1
    b -= 1
    unite(a,b)
ans = 0
for i in range(n):
    ans = max(ans,-par[i])
print(ans)
# Original solution
# 接続最大クラスタのサイズが答え？

N, M = [int(x) for x in input().split(' ')]

from collections import defaultdict
d = defaultdict(set)

for i in range(M):
    a, b = [int(x) for x in input().split(' ')]
    d[a].add(b)
    d[b].add(a)

def bfs(i):
    obs = {i}
    passed = {i}

    # get next_obs
    before = {i}
    while True:
        after = set()
        for i in before:
            after |= d[i]
        before = after - passed
        passed |= after
        if len(after) == 0:
            return len(passed)

print((max([bfs(i) for i in range(1, N+1)])))

# Optimized solution
# 接続最大クラスタのサイズが答え？

N, M = [int(x) for x in input().split(' ')]

# from collections import defaultdict
# d = defaultdict(set)

# for i in range(M):
#     a, b = [int(x) for x in input().split(' ')]
#     d[a].add(b)
#     d[b].add(a)

class UF:
    def __init__(self):
        self.r = [-1] * N

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

    def union(self, i, j):
        i -= 1
        j -= 1
        ri = self.root(i)
        rj = self.root(j)
        if ri == rj:
            return
        else:
            if not self.r[ri] < self.r[rj]:
                ri, rj = rj, ri
            self.r[ri] += self.r[rj]
            self.r[rj] = ri
            return

    def max_size(self):
        return -min(self.r)

uf = UF()
for i in range(M):
    uf.union(*[int(x) for x in input().split(' ')])
print((uf.max_size()))
# Original solution
N,M = list(map(int,input().split()))
ls = [0 for _ in range(N)]
flg = 1
for i in range(M):
    a,b = list(map(int,input().split()))
    if ls[a-1] == 0 and ls[b-1] == 0:
        ls[a-1]=flg
        ls[b-1]=flg
        flg+=1
    elif ls[a-1] == 0:
        ls[a-1] = ls[b-1]
    elif ls[b-1] == 0:
        ls[b-1] = ls[a-1]
    else:
        check = ls[b-1]
        ls[b-1] = ls[a-1]
        for l in range(len(ls)):
            if ls[l] == check:
                ls[l]=ls[a-1]
import collections
c = collections.Counter(ls)
plan = c.most_common()
ans = 0
if plan[0][0] == 0 and M != 0:
    ans = plan[1][1]
elif M ==0:
    ans =1
else:
    ans = plan[0][1]
print(ans)

# Optimized solution
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 j in range(n):
    check = uf.size(j)
    if ans < check:
        ans = check
print(ans)
# Original solution
import sys
import math
from collections import deque
n,m = list(map(int, input().split()))

q = [0]*(n+1)

graph = [[] for _ in range(n+1)]
for i in range(m):
    a, b = list(map(int, input().split()))
    graph[a].append(b)
    graph[b].append(a)

dist = [-1] * (n+1)
dist[0] = 0
dist[1] = 0

if m == 0:
    print((1))
    exit()

d = deque()
a = 0
for l,j in enumerate(q):
    b = set()
    if j == 0:
        d.append(l)
        while d:
            v = d.popleft()
            b.add(v)
            q[v] = -1

            for i in graph[v]:
                b.add(i)
                q[i] = -1

                if dist[i] != -1:
                    continue
                dist[i] = dist[v] + 1
                d.append(i)
        a = max(a,len(b))
print(a)

# Optimized solution
import sys
import math
from collections import deque
n,m = list(map(int, input().split()))

q = [0]*(n+1)

graph = [[] for _ in range(n+1)]
for i in range(m):
    a, b = list(map(int, input().split()))
    graph[a].append(b)
    graph[b].append(a)

dist = [-1] * (n+1)
dist[0] = 0
dist[1] = 0

# if m == 0:
#     print(1)
#     exit()

d = deque()
a = 0
for l,j in enumerate(q):
    b = set()
    if j == 0:
        d.append(l)
        while d:
            v = d.popleft()
            b.add(v)
            q[v] = -1

            for i in graph[v]:
                b.add(i)
                q[i] = -1

                if dist[i] != -1:
                    continue
                dist[i] = dist[v] + 1
                d.append(i)
        a = max(a,len(b))
print(a)
# Original solution
import collections
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 7)

class UnionFindPathCompression():
    def __init__(self, n):
        self.parents = list(range(n))
        self.rank = [1]*n
        self.size = [1]*n
        

    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 union(self, x, y):
        px = self.find(x)
        py = self.find(y)

        if px == py:
            return
        else:
            if self.rank[px] < self.rank[py]:
                self.parents[px] = py
                self.size[py] += self.size[px]
            else:
                self.parents[py] = px
                self.size[px] += self.size[py]
                #ランクの更新
                if self.rank[px] == self.rank[py]:
                    self.rank[px] += 1


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

for i in range(n):
    ufpc.find(i)

c = collections.Counter(ufpc.parents)
score_sorted = sorted(list(c.items()), key=lambda x:-x[1])
print((score_sorted[0][1]))


# Optimized solution
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10 ** 7)

class UnionFindPathCompression():
    def __init__(self, n):
        self.parents = list(range(n))
        self.rank = [1]*n
        self.size = [1]*n
        

    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 union(self, x, y):
        px = self.find(x)
        py = self.find(y)

        if px == py:
            return
        else:
            if self.rank[px] < self.rank[py]:
                self.parents[px] = py
                self.size[py] += self.size[px]
            else:
                self.parents[py] = px
                self.size[px] += self.size[py]
                #ランクの更新
                if self.rank[px] == self.rank[py]:
                    self.rank[px] += 1


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

for i in range(n):
    ufpc.find(i)
    
counter = {}
for v in ufpc.parents:
    if v in counter:
        counter[v] += 1
    else:
        counter[v] = 1

ans = 0
for key,val in list(counter.items()):
    ans = max(ans, val)
print(ans)
# Original solution
# union find, bfs

import sys
from collections import deque
si = sys.stdin.readline


def main():
    [n, m] = [int(e) for e in si().split()]
    if not m:
        print((1))
        return

    visited = [False]*(n+1)
    fm = [set() for _ in range(n+1)]
    mx, comp = 0, 0

    while m:
        m -= 1
        [a, b] = [int(e) for e in si().split()]
        fm[a].add(b)
        fm[b].add(a)

    for i in range(1, n+1):
        if visited[i]:
            continue
        visited[i] = True
        comp += 1
        q = deque()
        q.append(i)
        cnt = 1
        while q:
            curr = q.popleft()
            for e in fm[curr]:
                if not visited[e]:
                    visited[e] = True
                    q.append(e)
                    cnt += 1
        mx = max(mx, cnt)
    print(mx)

if __name__ == '__main__':
    main()


# Optimized solution
# union find, bfs

import sys
from collections import deque
si = sys.stdin.readline


def main():
    [n, m] = [int(e) for e in si().split()]
    if not m:
        print((1))
        return

    visited = [False]*(n+1)
    fm = [set() for _ in range(n+1)]
    mx = 0

    while m:
        m -= 1
        [a, b] = [int(e) for e in si().split()]
        fm[a].add(b)
        fm[b].add(a)

    for i in range(1, n+1):
        if visited[i]:
            continue
        visited[i] = True
        q = deque()
        q.append(i)
        cnt = 1
        while q:
            curr = q.popleft()
            for e in fm[curr]:
                if not visited[e]:
                    visited[e] = True
                    q.append(e)
                    cnt += 1
        mx = max(mx, cnt)
    print(mx)

if __name__ == '__main__':
    main()
# Original solution
from collections import Counter 

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

    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 union(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
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
            self.par[y] = x

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

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

for _ in range(M):
    x, y = list(map(int, input().split()))
    uf1.union(x-1,y-1)

for i in range(N):
    uf1.find(i) 
c = list(Counter(uf1.par).values())
print((max(c)))

# Optimized solution
import sys
sys.setrecursionlimit(500*500)

N, M = list(map(int, input().split()))
UF = [-1] * (N+1)
def find(x):
    global UF
    if UF[x] == -1:
        return x
    else:
        UF[x] = find(UF[x])
        return UF[x]
def union(x,y):
    global UF
    xx = find(x)
    yy = find(y)
    if UF[x] == UF[y] == -1:
        UF[y] = xx
        return
    elif xx == yy:
        return 
    else:
        UF[yy] = xx
        return 
    
for _ in range(M):
    x, y = list(map(int, input().split()))
    union(x,y)
    
cnt = [0] * (N+1)
for i in range(1,N+1):
    z = find(i)
    cnt[z] += 1

print((max(cnt)))