stablegradients/pie-gem5-bysrc huggingface dataset:
// Original C++ solution (slower):
#include<bits/stdc++.h>

using namespace std;

#define ll long long

const ll mod=1e9+7;

const int maxn=2e5+100;

ll fac[maxn],f[maxn];

ll fpow(ll x, ll c) { ll res = 1; for (; c > 0; c >>= 1) { if (c & 1) res = res * x % mod; x = x * x % mod; }return res; }



ll comb(ll x, ll y) {

	return fac[x] * fpow(fac[y] * fac[x - y] % mod, mod - 2) % mod;

}



void init(int n) { fac[0] = fac[1] = 1; for(int i=2;i<=n;i++) fac[i] = i * fac[i - 1] % mod; }

int main(){

	init(200010);

	int n,m,a,b;

	cin>>n>>m>>a>>b;

	int x=n-a,y=b+1;

	ll ans=0;

  while(x>=1&&y<=m){

		ans=(ans+comb(x+y-1-1,x-1)*comb(n+m-x-y,n-x))%mod;

		x--,y++;

	}

	cout<<ans<<'\n';

}

		

// Optimized solution to the same problem (faster):
#include<iostream>

#include<cstdio>

#include<cstring>

#include<cctype>

#include<algorithm>

#define int long long

using namespace std;

inline int read(){

	int x=0,w=0;char c=getchar();

	while(!isdigit(c))w|=c=='-',c=getchar();

	while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();

	return w?-x:x;

}

namespace star{

	const int maxn=1e5,mod=1e9+7;

	int h,w,a,b;

	inline int fpow(int a,int b){

		int ans=1;

		for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;

		return ans;

	}

	int mul[maxn*2+10],inv[maxn*2+10];

	inline void pre(int n){

		mul[0]=inv[0]=1;

		for(int i=1;i<=n;i++)

			mul[i]=mul[i-1]*i%mod;

		inv[n]=fpow(mul[n],mod-2);

		for(int i=n-1;i;i--)

			inv[i]=inv[i+1]*(i+1)%mod;

	}	

	inline int C(int n,int m){

		return mul[n]*inv[n-m]%mod*inv[m]%mod;

	}

	inline void work(){

		h=read(),w=read(),a=read(),b=read();

		pre(h+w);

		int ans=0;

		for(int i=b+1;i<=w;i++)

			ans=(ans+C(h-a+i-2,i-1)*C(w+a-i-1,a-1)%mod)%mod;

		printf("%lld\n",ans);

	}

}

signed main(){

	star::work();

	return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include<iostream>

#include <vector>

#include <list>

#include<set>

#include<algorithm>

#define pb emplace_back

#define ll long long

#define mp make_pair

#define PI acos(-1)

using namespace std;

ll n,m;

vector<ll>g[200005],v;

bool visit[200005];

set<ll>s;

void dfs(ll node)

{

    visit[node]=1;

    s.insert(node);

    for(auto child:g[node])

    {

        if(!visit[child])

            dfs(child);

    }

}

int main()

{

    ios_base::sync_with_stdio(false);

    cin.tie(0);

    cout.tie(0);

    ll i,j,k,x,y,z,t,b,c,d,a,l,r;

    cin>>n>>m;

    for(i=1; i<=m; i++)

    {

        cin>>a>>b;

        g[a].pb(b);

        g[b].pb(a);

    }

    for(i=1; i<=n; i++)

    {



        if(!visit[i])

        {

            dfs(i);

            v.pb(s.size());

            s.clear();

        }

    }

    sort(v.rbegin(),v.rend());

    if(v.size()==1)

        cout<<v[0]<<'\n';

    else

        cout<<(v[0]-v[1])+v[1]<<'\n';

}


























// Optimized solution to the same problem (faster):
#include <iostream>



using namespace std;

const int maxn = 200500;

int f[maxn];

int num[maxn];

// int x[maxn];

// int y[maxn];

int n,m,x,y;

int find_(int x){

    if(f[x]==x)return x;

    return f[x]=find_(f[x]);

}

void init() {

    for(int i = 0;i <= n;i++)

        f[i] = i;

}

void merge(int x,int y) {

    int fax = find_(x);

    int fay = find_(y);

    if(fax!=fay)

        f[fay] = fax;

}



int ans = 0;

int main()

{

    scanf("%d%d",&n,&m);    //n个点m组

    init();

    for(int i=0;i < m;i++) {

        scanf("%d%d",&x,&y);

        merge(x,y);

    }

    for(int i = 1;i <= n;i++) 

        num[find_(i)]++;

    for(int i = 1;i <= n;i++) {

        if(ans < num[i])

            ans = num[i];

    }

    printf("%d\n",ans);

    return 0;

}


// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

using namespace std;

struct unionfind{

    int par[200100];

    int rank[200100];



    void init(int n){

        for(int i=0;i<n;i++){

            par[i]=i;

            rank[i]=0;

        }

    }



    int find(int x){

        if(par[x] == x) return x;

        else return par[x] = find(par[x]);

    }



    void unite(int x, int y){

        x = find(x);

        y = find(y);

        if(x == y) return;

        if(rank[x]<rank[y]) par[x] = y;

        else{

            par[y] = x;

            if(rank[x] == rank[y]) rank[x]++;

        }

    }



    bool same(int x, int y){

        if(find(x)==find(y)) return true;

        else return false;

    }

};

int main(){

   long long n, m, ans=0, i, cnt[200100]={0}, a[200100], b[200100];

   unionfind g;

   cin >> n >> m;

   g.init(n);

   for(i=0;i<m;i++){

       cin >> a[i] >> b[i];

       g.unite(a[i], b[i]);

   }



   for(i=0;i<n;i++){

      cnt[g.find(i)]++;

   }

   for(auto x : cnt){

       ans=max(ans, x);

   }

   cout << ans <<endl;



   return 0;

}

// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

using namespace std;

struct unionfind{

    int par[200100];

    int rank[200100];



    void init(int n){

        for(int i=0;i<n;i++){

            par[i]=i;

            rank[i]=0;

        }

    }



    int find(int x){

        if(par[x] == x) return x;

        else return par[x] = find(par[x]);

    }



    void unite(int x, int y){

        x = find(x);

        y = find(y);

        if(x == y) return;

        if(rank[x]<rank[y]) par[x] = y;

        else{

            par[y] = x;

            if(rank[x] == rank[y]) rank[x]++;

        }

    }



    bool same(int x, int y){

        if(find(x)==find(y)) return true;

        else return false;

    }

};

int main(){

   long long n, m, ans=0, i, a[200100], b[200100];

   map<long long, long long> mp;

   unionfind g;

   cin >> n >> m;

   g.init(n);

   for(i=0;i<m;i++){

       cin >> a[i] >> b[i];

       g.unite(a[i], b[i]);

   }



   for(i=0;i<n;i++){

      mp[g.find(i)]++;

   }

   for(auto x : mp){

       ans=max(ans, x.second);

   }

   cout << ans <<endl;



   return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

using namespace std;

//NOT TO SUBMIT IN WRONG PLACE OR BY WRONG COMPILER

//ABC 177, task a, 2020/08/29

//ms,

/*

添え字が1-indexになってるのに気づいてなくて草。

入力時に1小さいものをとるか、最初に配列を一つ大きくとるか。

*/



int main(void){

  int N, M;

  vector<vector<int>> friends(200000);

  queue<int> next;

  vector<int> dist(200000, -1);

  int ans=1;

  int group_size;



  cin >>N >>M;



  //input

  for(int i=0;i<M;i++){

    int u, v;

    cin >>u >>v;

    friends[u-1].push_back(v-1);

    friends[v-1].push_back(u-1);

  }



  //bfs

  for(int v=0;v<N;v++){

    if(dist[v] != -1) continue;



    dist[v]=0;

    next.push(v);

    group_size=1;



    while(next.size()!=0){

      int u = next.front();

      next.pop();



      for(long unsigned int i=0;i<friends[u].size();i++){

        int w = friends[u][i];

        if(dist[w] != -1) continue;



        dist[w] = dist[u]+1;

        next.push(w);

        group_size++;

      }

    }//end while

    ans = max(ans, group_size);

  }//end for



  cout <<ans<<endl;

  return 0;

}

/*

お手上げ！！！

解法はunion find木とBFSの二通り。

BFSは連結な重みなしのグラフの最短路問題を線形時間で解けるのが特徴。

また、未探索の頂点が消えるまでやり続ければ連結成分の様子がなんでもわかる。

今回は後者の特徴を使う。

友達のネットワークのグラフの、最大の連結成分の成分個のグループがあればよい。

*/


// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

using namespace std;

//NOT TO SUBMIT IN WRONG PLACE OR BY WRONG COMPILER

//ABC 177, task a, 2020/08/30

//ms,

/*

*/



struct UnionFind{

  //各頂点の親のid。親がいないなら、子の頂点数に-1をかけた値。このvectorで木を表現。

  vector<int> r;



  //コンストラクタ

  UnionFind(int N){

    r = vector<int>(N, -1);

  }



  //入力：頂点ラベル。出力：最上位の親id。入力自身でもありうる。ついでにグラフの縮約も。

  int root(int v){

    if(r[v]<0) return v;

    else{

      r[v]=root(r[v]);

      return r[v];

    }

  }



  //入力：二つの頂点ラベル。出力：併合可否。(もともと同じ集合ならfalse)。

  //入力の頂点についてはグラフの縮約をする。さらにそれぞれの頂点の集合を併合する。

  bool unite(int x, int y){

    x=root(x);

    y=root(y);

    //すでに同所属

    if(x==y) return false;



    //異所属の場合、併合。小さい方を大きい方に入れる(これは大事)

    if(abs(r[x]) < abs(r[y]) ) swap(x, y);

    r[x] += r[y];

    r[y] = x;

    return true;

  }



  int size(int x){

    return -r[root(x)];

  }

};



int main(void){

  int N, M;

  int ans=1;



  cin >>N >>M;

  UnionFind friends(N);

  for(int i=0;i<M;i++){

    int a, b;

    cin >>a >>b;

    friends.unite(a-1, b-1);

  }



  for(int i=0;i<N;i++){

    ans = max(ans, friends.size(i));

  }



  cout <<ans<<endl;

  return 0;

}

/*

幅優先探索だけじゃなくて、ユニオンファインド木もやるよー。

ユニオンファインド木はグループわけを表現できます。

所属判定も当然できます。超高速で、logNよりさらに速いです(適切な実装のもと)。

グループの大きさも返せます。

*/


// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

using namespace std;

//NOT TO SUBMIT IN WRONG PLACE OR BY WRONG COMPILER

//ABC 177, task a, 2020/08/29

//ms,

/*

添え字が1-indexになってるのに気づいてなくて草。

入力時に1小さいものをとるか、最初に配列を一つ大きくとるか。

*/



int main(void){

  int N, M;

  vector<vector<int>> friends(200001);

  queue<int> next;

  vector<int> dist(200001, -1);

  int ans=1;

  int group_size;



  cin >>N >>M;



  //input

  for(int i=0;i<M;i++){

    int u, v;

    cin >>u >>v;

    friends[u].push_back(v);

    friends[v].push_back(u);

  }



  //bfs

  for(int v=0;v<N;v++){

    if(dist[v] != -1) continue;



    dist[v]=0;

    next.push(v);

    group_size=1;



    while(next.size()!=0){

      int u = next.front();

      next.pop();



      for(long unsigned int i=0;i<friends[u].size();i++){

        int w = friends[u][i];

        if(dist[w] != -1) continue;



        dist[w] = dist[u]+1;

        next.push(w);

        group_size++;

      }

    }//end while

    ans = max(ans, group_size);

  }//end for



  cout <<ans<<endl;

  return 0;

}

/*

お手上げ！！！

解法はunion find木とBFSの二通り。

BFSは連結な重みなしのグラフの最短路問題を線形時間で解けるのが特徴。

また、未探索の頂点が消えるまでやり続ければ連結成分の様子がなんでもわかる。

今回は後者の特徴を使う。

友達のネットワークのグラフの、最大の連結成分の成分個のグループがあればよい。

*/


// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

using namespace std;

//NOT TO SUBMIT IN WRONG PLACE OR BY WRONG COMPILER

//ABC 177, task a, 2020/08/30

//ms,

/*

*/



struct UnionFind{

  //各頂点の親のid。親がいないなら、子の頂点数に-1をかけた値。このvectorで木を表現。

  vector<int> r;



  //コンストラクタ

  UnionFind(int N){

    r = vector<int>(N, -1);

  }



  //入力：頂点ラベル。出力：最上位の親id。入力自身でもありうる。ついでにグラフの縮約も。

  int root(int v){

    if(r[v]<0) return v;

    else{

      r[v]=root(r[v]);

      return r[v];

    }

  }



  //入力：二つの頂点ラベル。出力：併合可否。(もともと同じ集合ならfalse)。

  //入力の頂点についてはグラフの縮約をする。さらにそれぞれの頂点の集合を併合する。

  bool unite(int x, int y){

    x=root(x);

    y=root(y);

    //すでに同所属

    if(x==y) return false;



    //異所属の場合、併合。小さい方を大きい方に入れる(これは大事)

    if(abs(r[x]) < abs(r[y]) ) swap(x, y);

    r[x] += r[y];

    r[y] = x;

    return true;

  }



  int size(int x){

    return -r[root(x)];

  }

};



int main(void){

  int N, M;

  int ans=1;



  cin >>N >>M;

  UnionFind friends(N);

  for(int i=0;i<M;i++){

    int a, b;

    cin >>a >>b;

    friends.unite(a-1, b-1);

  }



  for(int i=0;i<N;i++){

    ans = max(ans, friends.size(i));

  }



  cout <<ans<<endl;

  return 0;

}

/*

幅優先探索だけじゃなくて、ユニオンファインド木もやるよー。

ユニオンファインド木はグループわけを表現できます。

所属判定も当然できます。超高速で、logNよりさらに速いです(適切な実装のもと)。

グループの大きさも返せます。

*/


// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>



using namespace std;



#define MAXN 200005



int N, M;

int cnt;

vector<int> adj[MAXN];

vector<bool> visited;



void dfs(int v)

{

    if (visited[v])

        return;

    

    visited[v] = true;

    cnt++;

    for (auto u : adj[v])

        dfs(u);

}



int main()

{

    cin >> N >> M;

    visited.assign(N + 1, false);

    for (int i = 0; i < M; i++) {

        int a, b;

        cin >> a >> b;

        adj[a].push_back(b);

        adj[b].push_back(a);

    }

    int ans = 0;

    for (int i = 1; i <= N; i++) {

        cnt = 0;

        dfs(i);

        ans = max(ans, cnt);

    }

    cout << ans << '\n';

    return 0;

}

// Optimized solution to the same problem (faster):
#include <iostream>

#include <vector>

#include <set>



using namespace std;



#define MAXN 200005



int N, M;

vector<int> parent;

vector<int> sz;



void init()

{

    parent.assign(N+1, 1);

    sz.assign(N+1, 1);

    for (int i = 1; i <= N; i++) {

        parent[i] = i;

    }

}



int find(int v)

{

    if (parent[v] == v)

        return v;

    

    return find(parent[v]);

}



bool same(int u, int v)

{

    return find(u) == find(v);

}



void join(int u, int v)

{

    u = find(u);

    v = find(v);

    

    if (u == v)

        return;

    

    if (sz[u] > sz[v])

        swap(u, v);

    

    parent[u] = v;

    sz[v] += sz[u];

}



int main()

{

    cin >> N >> M;

    init();

    for (int i = 0; i < M; i++) {

        int a, b;

        cin >> a >> b;

        join(a, b);

    }

    set<int, greater<int> > s;

    for (int i = 1; i <= N; i++) {

        s.insert(sz[i]);

    }

    cout << *s.begin() << '\n';

    return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
//写経　おぶ　すぬけ

#include <bits/stdc++.h>

using namespace std;



#define rep(i,n) for (int i = 0 ; i <  (n) ; ++i) 

struct UnionFind {

	vector<int> d;

	UnionFind(int n=0): d(n,-1) {} //このコロンってなんですか

	int find(int x){

		if (d[x] < 0) return x; //x:根

		return d[x] = find(d[x]);//x:子 -> xの親の根を返す

	}

	bool unite(int x, int y) {

		x = find(x); y = find(y);//根同士をくっつける。

		if ( x ==  y) return false ; 

		/*連結成分が一致してるんだったらfalse返す。

			minimum spanning tree (クラスカル法と言うのをやるときに、くっつけたかどうかを取れると嬉しい.

			chmin/chmaxみたいなノリかな

		 */

		if ( d[x] > d[y]) swap(x,y);//xの方が大きいようにする（ただしサイズは負デモっているので不等号が逆）

		d[x] += d[y] ; //xにyをくっつける.

		d[y] = x;

		return true; 

	}

	bool same(int x, int y) { return find(x) == find(y) ; }

	int size(int x) { return -d[find(x)];}

};



int deg[100005];

vector<int> to[100005] ;

int main(){

	int n, m;

	cin >>  n>> m;



	UnionFind uf(n);

	rep(i,m){

		int a, b;

		cin >>  a>> b;

		uf.unite(a,b);

	}

	int ans = 0;

	rep(i,n){

		ans = max(ans, uf.size(i));

	}

	cout << ans << endl;

}






// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>



using namespace std;

using ll =long long;

typedef pair<int,int> P;

#define SORT(a) sort((a).begin(),(a).end())

#define REV(a) reverse((a).begin(),(a).end())

#define For(i, a, b)    for(int i = (a) ; i < (b) ; ++i)

#define rep(i, n)       For(i, 0, n)

#define debug(x)  cerr << #x << " = " << (x) << endl;

template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return true; } return false; }

template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return true; } return false; }



void coY() {cout <<"Yes"<<endl;}

void coN(){cout <<"No"<<endl;}

 

//Write From this Line



//const ll mod = 1e9+7;

//const ll mod = 998244353;

//AtCoder解説動画(https://www.youtube.com/watch?v=TdR816rqc3s&feature=youtu.be)からもらった

// (https://atcoder.jp/contests/abc157/submissions/10472433)

struct UnionFind {

	vector<int> d;

	UnionFind(int n=0): d(n,-1) {} //このコロンってなんですか

	int find(int x){

		if (d[x] < 0) return x; //x:根

		return d[x] = find(d[x]);//x:子 -> xの親の根を返す

	}

	bool unite(int x, int y) {

		x = find(x); y = find(y);//根同士をくっつける。

		if ( x ==  y) return false ; 

		/*連結成分が一致してるんだったらfalse返す。

			minimum spanning tree (クラスカル法と言うのをやるときに、くっつけたかどうかを取れると嬉しい.

			chmin/chmaxみたいなノリかな

		 */

		if ( d[x] > d[y]) swap(x,y);//xの方が大きいようにする（ただしサイズは負デモっているので不等号が逆）

		d[x] += d[y] ; //xにyをくっつける.

		d[y] = x;

		return true; 

	}

	bool same(int x, int y) { return find(x) == find(y) ; }

	int size(int x) { return -d[find(x)];}

};

int main()

{

	int n, m;

	cin >> n >> m;

	UnionFind uf(n);

	rep(i,m){

		int a, b;

		cin >> a >> b;

		uf.unite(a,b);

	}

	int ans = 0;

	rep(i,n){

		ans = max(ans,uf.size(i));

	}

	cout << ans << endl;

}


// Fastest known solution for this problem:
// Original C++ solution (slower):
#include "bits/stdc++.h"

using namespace std;



const int N=2e5+20;



int n,m,u,v,cnt,ans;

vector <int> adj[N];

set <pair <int,int>> s;

bool vis[N];



void dfs(int node)

{

	cnt++;

	vis[node]=true;

	for(auto child:adj[node])

		if(not vis[child]) dfs(child);

}



int main()

{

	scanf("%d%d",&n,&m);

	while(m--)

	{

		scanf("%d%d",&u,&v);



		if(u>v) swap(u,v);

		if(s.find({u,v})!=s.end()) continue;



		adj[u].push_back(v);

		adj[v].push_back(u);

		s.insert({u,v});

	}



	for(int i=1;i<=n;i++)

	{

		if(vis[i]) continue;

		cnt=0;

		dfs(i);

		ans=max(ans,cnt);

	}



	printf("%d",ans);

}

// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

using namespace std;



const int N=2e5+20;

int n,m,u,v,p[N],sz[N],ans;



void init() {for(int i=1;i<=n;i++) p[i]=i,sz[i]=1;}



int root(int i) {return p[i]==i?i:p[i]=root(p[i]);}



bool cycle(int a,int b) {return root(a)==root(b);}



void join(int a,int b)

{

	a=root(a),b=root(b);

	if(sz[a]<sz[b]) swap(a,b);

	p[b]=p[a],sz[a]+=sz[b];

}



int main()

{

	scanf("%d%d",&n,&m);



	init();

	for(int i=0;i<m;i++)

	{

		scanf("%d%d",&u,&v);

		if(not cycle(u,v)) join(u,v);

	}



	for(int i=1;i<=n;i++) ans=max(ans,sz[root(i)]);

	cout<<ans;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;



ll n, m;

vector <ll> adjlist[200010];



int main() {

	cin>>n>>m;

	ll x, y;

	set < pair<ll, ll> > s;

	for (int i=0; i<m; i++) {

		cin>>x>>y;

		if (x>y) swap(x, y);

		if (s.count(make_pair(x, y))==0) {

			adjlist[x].push_back(y);

			adjlist[y].push_back(x);

			s.insert(make_pair(x, y));

		}

	}

	ll ans=0, temp=0;

	bool vis[n+1];

	memset(vis, 0, sizeof(vis));

	for (int i=1; i<=n; i++) {

		if (!vis[i]) {

			ans=max(temp, ans);

			temp=0;

			queue <ll> q;

			vis[i]=1;

			q.push(i);

			while (!q.empty()) {

				temp++;

				for (auto j: adjlist[q.front()]) {

					if (!vis[j]) {

						vis[j]=1;

						q.push(j);

					}

				}

				q.pop();

			}

		}

	}

	ans=max(temp, ans);

	cout<<ans<<endl;

}

// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

using namespace std;



const int MAXN=2*1e5+10;

const int MOD=1e9+7;



int pre[MAXN];

int nodenum[MAXN];



int Find(int a){

    if(pre[a]!=a)

        pre[a]=Find(pre[a]);

    return pre[a];

}



void join(int x,int y) {

    int fx=Find(x),fy=Find(y);

    if(fx!=fy){

        pre[fy]=fx;

        nodenum[fx]+=nodenum[fy];

    }

    else return;

}



int main() {

    int n,m,a,b,ans=0;

    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;i++) pre[i]=i,nodenum[i]=1;

    for(int i=1;i<=m;i++){

        scanf("%d%d",&a,&b);

        join(a,b);

    }

    for(int i=1;i<=n;i++){

        if(pre[i]==i){

            ans=max(nodenum[i],ans);

        }

    }

    cout<<ans<<endl;

    return 0;

}


// Fastest known solution for this problem:
// Original C++ solution (slower):
#include<bits/stdc++.h>

using namespace std;



int n,m;

int pre[200050];

int b[200050] = {0};



void init()

{

	for(int i = 0;i<=200050;++i)

	    pre[i] = i;

}



int find(int x)

{

	if(x == pre[x]) return x;

	int root = find(pre[x]);//压缩路径 

	return pre[x] = root;

}



void join(int x,int y)

{

	int fx = find(x),fy = find(y);

	

	if(fx != fy){

		pre[fx] = fy;

	}

}



int main()

{

    ios::sync_with_stdio(false);

    cin.tie(0);

    init();

	int n,m;

	cin>>n>>m;

	for(int i = 1;i<=m;++i){

		int x,y;

		cin>>x>>y;

		join(x,y);

		

//		for(int i = 1;i<=n;++i){

//			cout<<pre[i]<<" ";

//		}

//		cout<<endl;

	}

	for(int i = 1;i<=n;++i){

		b[find(i)]++;

	}

	int maxn = 0;

	for(int i = 1;i<=n;++i)

	    if(b[i]>maxn) maxn = b[i];

	    

	cout<<maxn<<endl;

	return 0;

}

// Optimized solution to the same problem (faster):
#include<bits/stdc++.h>

using namespace std;

#define IO ios::sync_with_stdio(false),cin.tie(0);

#define ll long long

#define inf 0x3f3f3f3f

const int N=2e5+5;

//set<string>b;

//set<string>::iterator it;

int f[N],c[N];

int getfa(int u)

{

	return f[u]==u?u:f[u]=getfa(f[u]);

}

void merge(int u,int v)

{

	int f1,f2;

	f1=getfa(u);f2=getfa(v);

	if(f1!=f2) f[f2]=f1;

}

int main()

{

	IO;

	int n,m,i,u,v,ans=0;

	cin>>n>>m;

	for(i=1;i<=n;i++) f[i]=i;

	for(i=1;i<=m;i++)

	{

		cin>>u>>v;

		merge(u,v);

	}

	for(i=1;i<=n;i++)

	{

		c[getfa(i)]++;

	}

	for(i=1;i<=n;i++)

	{

		ans=max(ans,c[i]);

	}

	cout<<ans<<endl;

    return 0;

}

// Fastest known solution for this problem:
// Original C++ solution (slower):
#include<bits/stdc++.h>



using namespace std;

using   ll=long long;



#define fast            ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)



#define endl            "\n"

#define pb              push_back

#define mp              make_pair

#define F               first

#define S               second

#define int             long long int

#define pll                pair<int , int>



#define ALL(v)          v.begin(),v.end()

#define ALLR(v)         v.rbegin(),v.rend()

#define pii             3.14159265358979323

#define inf             LLONG_MAX

#define ones(x)         __builtin_popcount(x)

#define fill(a,b)       memset(a,b,sizeof(a))

#define mod             1000000007

#define hell            998244353

const int maxn = 2e5 + 1;



vector<vector<int>> a(maxn);



int tot;



int vis[maxn];



void dfs(int u) {

	vis[u] = 1;

	

	tot++;

	

	for(auto v : a[u]) {

		if(!vis[v])  {

			dfs(v);

		}

	}

}



void solve()

{

	int n , m;

	

	cin >> n >> m;

	

	for(int i = 0; i < m; ++i) {

		int x , y;

		

		cin >> x >> y;

		

		a[x].pb(y);

		a[y].pb(x);

	}

	

	int ans = 0;

	

	for(int i = 1; i <= n; ++i) {

		if(!vis[i]) {

			tot = 0;

			

			dfs(i);

			

			//cout << i <<" " << tot << endl;

			

			ans = max(ans , tot);

		}

	}

	

	cout << ans;

}



signed main() {

    fast;



    int t = 1;

   

    //cin >> t;



    while(t--) {

        solve();

    }



    return 0;

}

// Optimized solution to the same problem (faster):
#include<bits/stdc++.h>

using namespace std;



#define ll long long int

#define pi 3.141592653589793238

#define fast ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)

#define mod 1000000007

int val=0,ans=INT_MIN;

void dfs(int node,vector<vector<int>> &adj,vector<bool> &visited)

{

    visited[node]=true;

    val++;

    for(auto u:adj[node])

        if(!visited[u])

            dfs(u,adj,visited);

}

int main(){

	fast;

    int n,m;

    cin>>n>>m;

    vector<vector<int>> adj(n+1);

    vector<bool> visited(n+1,false);

    for(int i=0;i<m;i++)

    {

        int a,b;

        cin>>a>>b;

        adj[a].push_back(b);

        adj[b].push_back(a);

    }

    for(int i=1;i<=n;i++)

    {

        val=0;

        if(!visited[i])

           dfs(i,adj,visited);

        ans=max(ans,val);

    }

    cout<<ans;

	return 0;

}


// Fastest known solution for this problem: