#include<bits/stdc++.h>

using Weight = int;
using Vertex = int;
struct Edge {Vertex from, to;};
using Graph = std::vector<std::vector<Edge>>;

// ???????????£?????????????§£ O(|V| + |E|)
std::pair<Graph, std::vector<Weight>> bridge(const Graph& G) {
  std::stack<Vertex> roots, S;
  std::vector<bool> inS(G.size());
  std::vector<int> num(G.size()), group(G.size());
  std::vector<Edge> bridge;
  std::vector<std::vector<Vertex>> bcc;
  int timer = 0;
  // Tarjan
  std::function<void(const Edge&)> dfs = [&](const Edge& cur){
    num[cur.to] = ++timer;
    S.push(cur.to); inS[cur.to] = true;
    roots.push(cur.to);
    for(const auto& nex: G[cur.to]) {
      if(!num[nex.to]) dfs(nex);
      else if(nex.to != cur.from && inS[nex.to]) while(num[roots.top()] > num[nex.to]) roots.pop();
    }
    if(roots.top() == cur.to) {
      bridge.push_back(cur);
      bcc.push_back(std::vector<Vertex>());
      while(true) {
        auto v = S.top(); S.pop(); inS[v] = false;
        bcc.back().push_back(v);
        group[v] = bcc.size() - 1;
        if(v == cur.to) break;
      }
      roots.pop();
    }
  };
  for(auto v = 0; v < G.size(); ++v) if(!num[v]) {
    dfs({v, v});
    bridge.pop_back();
  }
  // Forrest
  Graph F(bcc.size());
  for(auto e: bridge) {
    auto s = group[e.from];
    auto t = group[e.to];
    F[s].push_back({s, t});
    F[t].push_back({t, s});
  }
  std::vector<Weight> W;
  for(auto i: bcc) W.emplace_back(i.size());
  return std::make_pair(F, W);
}

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

Weight solve(const Graph& G) {
  Graph F; vector<Weight> W;
  tie(F, W) = bridge(G);

  vector<bool> used(F.size());
  vector<vector<Weight>> dp(2, vector<Weight>(F.size(), -1));
  function<Weight(Vertex, Vertex)> ignore, opt;

  ignore = [&](Vertex cur, Vertex pre) {
    if(~dp[0][cur]) return dp[0][cur];
    dp[0][cur] = 0;
    used[cur] = true;
    for(auto e: F[cur]) if(e.to != pre) dp[0][cur] += max(ignore(e.to, cur), opt(e.to, cur));
    return dp[0][cur];
  };

  opt = [&](Vertex cur, Vertex pre) {
    if(~dp[1][cur]) return dp[1][cur];
    dp[1][cur] = W[cur];
    used[cur] = true;
    for(auto e: F[cur]) if(e.to != pre) dp[1][cur] += ignore(e.to, cur);
    return dp[1][cur];
  };

  auto res = 0;
  for(auto v = 0; v < F.size(); ++v) if(!used[v]) {
    res += max(ignore(v, v), opt(v, v));
  }
  return res;
}

int main() {
  int N, M;
  cin >> N >> M;
  Graph G(N);
  for(auto i = 0; i < M; ++i) {
    Vertex s, t;
    cin >> s >> t;
    G[s].push_back({s, t});
    G[t].push_back({t, s});
  }

  cout << solve(G) << endl;
}