#include <bits/stdc++.h>
using namespace std;
template<typename G>
struct LowLink {
    const G &g;
    vector<int> used, ord, low;
    vector<int> articulation;
    vector<pair<int, int> > bridge;
    LowLink(const G &g):g(g) {};
    int dfs(int idx, int k, int par) {
        used[idx] = true;
        ord[idx] = k++;
        low[idx] = ord[idx];
        bool is_articulation = false;
        int cnt = 0;
        for (auto &to : g[idx]) {
            if (!used[to]) {
                ++cnt;
                k = dfs(to, k, idx);
                low[idx] = min(low[idx], low[to]);
                is_articulation |= ~par && low[to] >= ord[idx];
                if (ord[idx] < low[to]) bridge.emplace_back(minmax(idx, (int)to));
            } else if (to != par) {
                low[idx] = min(low[idx], ord[to]);
            }
        }
        is_articulation |= par == -1 && cnt > 1;
        if (is_articulation) articulation.push_back(idx);
        return k;
    };
    virtual void build() {
        used.assign(g.size(), 0);
        ord.assign(g.size(), 0);
        low.assign(g.size(), 0);
        int k = 0;
        for (int i = 0; i < g.size(); i++) {
            if (!used[i]) k = dfs(i, k, -1);
        }
    };
};
template<typename G>
struct TwoEdgeConnectComponents : LowLink<G> {
    using LL = LowLink<G>;
    vector<int> comp;
    TwoEdgeConnectComponents(const G &g) : LL(g) {};
    int operator[] (const int &k) {
        return comp[k];
    };
    void dfs(int idx, int par, int &k) {
        if (~par && this->ord[par] >= this->low[idx]) comp[idx] = comp[par];
        else comp[idx] = k++;
        for (auto &to : this->g[idx]) {
            if (comp[to] == -1) dfs(to, idx, k);
        }
    };
    void build(G &t) {
        LL::build();
        comp.assign(this->g.size(), -1);
        int k = 0;
        for (int i = 0; i < comp.size(); i++) {
            if (comp[i] == -1) dfs(i, -1, k);
        }
        t.resize(k);
        for (auto &e : this->bridge) {
            int x = comp[e.first], y = comp[e.second];
            t[x].push_back(y);
            t[y].push_back(x);
        }
    }
};
vector<vector<int> > g;
vector<vector<int> > t;
int node_sum[100005];
int memo[100010][2];
int dp(int i, int par){
    for(auto e : t[i]){
        if(e != par) dp(e, i);
    }
    int n1 = 0;
    int n2 = node_sum[i];
    for(auto e : t[i]){
        if(e == par) continue;
        n1 += max(memo[e][0], memo[e][1]);
        n2 += memo[e][0];
    }
    memo[i][0] = n1;
    memo[i][1] = n2;

    return max(n1, n2);
}

int main() {
    int n, m;
    cin >> n >> m;
    
    g.resize(n);
    for (int i = 0; i < m; i++) {
        int s, t;
        cin >> s >> t;
        g[s].push_back(t);
        g[t].push_back(s);
    }
    TwoEdgeConnectComponents<vector<vector<int> > > bcc(g);
    bcc.build(t);
    
    for (int i = 0; i < n; i++) {
        int u = bcc[i];
        node_sum[u]++;
    }
    
    int ans = 0;
    for (int i = 0; i < n; i++) {
        int u = bcc[i];
        if (memo[u][0] == 0 && memo[u][1] == 0) {
            ans += dp(u, -1);
        }
    }

    cout << ans << endl;
}

