#include <bits/stdc++.h>
using namespace std;

using ll = long long int;
using P = pair<int, int>;
using P3 = pair<int, P>;
using PP = pair<P, P>;
constexpr int INF = 1 << 30;
constexpr ll MOD = ll(1e9) + 7;
constexpr int di[] = {0, 1, 0, -1};
constexpr int dj[] = {1, 0, -1, 0};

struct UnionFindTree {
    int V;
    vector<int> par;
    vector<int> rank;
    vector<int> siz;

    void init(int n) {
        V = n;
        par.resize(n);
        rank.resize(n);
        siz.resize(n);
        for (int i = 0; i < n; i++) {
            par[i] = i;
            rank[i] = 0;
            siz[i] = 1;
        }
    }
    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;
        V--;
        if (rank[x] < rank[y]) swap(x, y);
        if (rank[x] == rank[y]) rank[x]++;
        par[y] = x;
        siz[x] += siz[y];
    }
    bool is_same(int x, int y) {
        return find(x) == find(y);
    }
    int size(int x) {
        x = find(x);
        return siz[x];
    }
};

int N, M;

vector<vector<int> > g, G;
vector<int> ord, low;
vector<bool> is_articulation, visited;
vector<P> bridges, non_bridges;

void dfs(int u, int par, int &k){
    ord[u] = k;
    k++;
    low[u] = ord[u];
    visited[u] = true;
    int cnt = 0;
    for(auto v : g[u]){
        // dfsの子ノードへ移動
        if(ord[v] == -1){
            cnt++;
            dfs(v, u, k);
            low[u] = min(low[u], low[v]);
            if(par!=-1 && ord[u] <= low[v]){
                is_articulation[u] = true;
            }
            if(ord[u] < low[v]){
                bridges.push_back(make_pair(min(u,v), max(u,v)));
            }else{
                non_bridges.push_back(make_pair(min(u,v), max(u,v)));
            }
        }
        // dfs木の後退辺を移動
        else if(v != par)low[u] = min(low[u], ord[v]);
    }
    if(par == -1){
        is_articulation[u] = cnt >= 2;  // 根はdfs木の次数が2以上ならば関節点
    }
}

void lowlink(){
    ord.assign(g.size(), -1);
    low.assign(g.size(), INF);
    visited.assign(g.size(), false);
    is_articulation.assign(g.size(), false);
    int k = 0;
    for(int i=0;i<int(g.size());i++){
        if(!visited[i]) dfs(i, -1, k);
    }
}

UnionFindTree uf;
map<int,int> id;
vector<int> val;
vector<vector<int> > dp;

int rec(int i, int c, int p){
    if(dp[i][c] != -1) return dp[i][c];
    visited[i] = true;
    int res = 0;
    for(auto &j : G[i]){
        if(j == p) continue;
        res += rec(j,1,i);
    }
    if(c == 1){
        int sum = val[i];
        for(auto &j : G[i]){
            if(j == p) continue;
            sum += rec(j,0,i); 
        }
        res = max(res, sum);
    }
    return dp[i][c] = res;
}

int main() {
    cin >> N >> M;
    g.resize(N);
    for(int i=0;i<M;i++){
        int s, t;
        cin >> s >> t;
        g[s].emplace_back(t);
        g[t].emplace_back(s);
    }
    lowlink();
    uf.init(N);
    for(auto &e : non_bridges){
        int s = e.first, t = e.second;
        uf.unite(s,t);
    }
    val.resize(uf.V);
    for(int i=0,idx=0;i<N;i++){
        if(id.find(uf.find(i))==id.end()){
            id[uf.find(i)] = idx;
            val[idx] = uf.size(i);
            idx++;
        }
    }
    G.resize(uf.V);
    for(auto &e : bridges){
        int s = uf.find(e.first), t = uf.find(e.second);
        G[id[s]].push_back(id[t]);
        G[id[t]].push_back(id[s]);
    }
    dp.resize(uf.V, vector<int>(2, -1));
    fill(visited.begin(), visited.end(), false);
    int ans = 0;
    for(int i=0;i<uf.V;i++){
        if(!visited[i]){
            ans += rec(i,1,-1);
        }
    }
    cout << ans << endl;
    return 0;
}



Measured memory_bytes: 27012