#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

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

int main() {
  string s;
  cin >> s;
  string w;
  int cnt = 0;
  while (cin >> w, w != "EOF_OF_TEXT") {
    if (w == s)
      cnt++;
  }
  cout << cnt << endl;
  return 0;
}

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;

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

int main() {
  string s;
  cin >> s;
  transform(s.begin(), s.end(), s.begin(), ::tolower);
  string w;
  int cnt = 0;
  while (cin >> w, w != "END_OF_TEXT") {
    transform(w.begin(), w.end(), w.begin(), ::tolower);
    if (s == w)
      cnt++;
  }
  cout << cnt << endl;
  return 0;
}


#include <cctype>
#include <cstring>
#include <ctype.h>
#include <iostream>
#include <locale>
#include <stdio.h>
#include <string>
using namespace std;

int main() {
  string s;
  cin >> s;
  int n = 0;
  while (true) {

    char *p;
    cin >> p;
    if (strcmp(p, "END_OF_TEXT")) {
      break;
    } else {
      for (int i = 0; i < strlen(p); i++) {
        p[i] = tolower(p[i]);
      }
      if (p == s) {
        n++;
      }
    }
  }

  cout << n << endl;
  return 0;
}

#include <cctype>
#include <cstring>
#include <ctype.h>
#include <iostream>
#include <locale>
#include <stdio.h>
#include <string>
using namespace std;

int main() {
  char s[20];
  cin >> s;
  int n = 0;
  while (true) {

    char p[20];

    cin >> p;

    if (strcmp(p, "END_OF_TEXT") == 0) {
      break;
    } else {
      for (int i = 0; i < strlen(p); i++) {
        p[i] = tolower(p[i]);
      }

      if (strcmp(p, s) == 0) {
        n += 1;
      }
    }
  }

  cout << n << endl;
  return 0;
}


#include <iostream>
#include <string>
using namespace std;

int main() {
  int i, j, count, wn, tn;
  string w, t;
  cin >> w;
  cin >> t;
  wn = w.size();
  tn = t.size();
  while (i != tn) {
    if (islower(w[i])) {
      w[i] = toupper(w[i]);
    }
    if (islower(t[j])) {
      t[j] = toupper(t[j]);
    }
    if (t[i] == '\\n') {
      break;
    }
    if (t[i] == w[j]) {
      j++;
      if (j == wn) {
        j = 0;
        count++;
      }
    }
    if (j != 0 && t[i] != w[j]) {
      j = 0;
    }
    i++;
  }
  cout << count << endl;
}

#include <iostream>
#include <string>
using namespace std;

int main() {
  string w, t;
  int i, count = 0;
  cin >> w;
  while (1) {
    cin >> t;
    if (t == "END_OF_TEXT") {
      break;
    }
    for (i = 0; i < t.length(); i++) {
      t[i] = tolower(t[i]);
    }
    if (t == w) {
      count++;
    }
  }
  cout << count << endl;
  return 0;
}


/*
 * http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_9_A
 */
#include <algorithm>
#include <iostream>
#include <string>

using namespace std;

template <typename T_char> T_char ToLower(T_char c) { return tolower(c); }

int main() {
  ios::sync_with_stdio(false);

  string W;
  getline(cin, W);

  int count = 0;
  while (true) {
    string line;
    getline(cin, line);
    transform(line.begin(), line.end(), line.begin(), ToLower<char>);

    if (line == "END_OF_TEXT") {
      break;
    }
    int pos = 0;
    while (true) {
      size_t found = line.find(W, pos);
      if (found == string::npos) {
        break;
      }
      count++;
      pos = found + 1;
    }
  }
  cout << count << endl;

  return 0;
}

/*
 * http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_9_A
 */
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

template <typename T_char> T_char ToLower(T_char c) { return tolower(c); }

int main() {
  ios::sync_with_stdio(false);

  string W;
  getline(cin, W);

  int count = 0;
  while (true) {
    string line;
    getline(cin, line);

    if (line == "END_OF_TEXT") {
      break;
    }
    transform(line.begin(), line.end(), line.begin(), ToLower<char>);
    stringstream ss(line);
    while (!ss.eof()) {
      string s;
      ss >> s;
      if (s == W) {
        count++;
      }
    }
  }
  cout << count << endl;

  return 0;
}


#include <algorithm>
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main() {
  int i, count = 0;
  char w[10], t[1000];
  int T_length;
  std::string s, W;

  cin >> w;
  for (i = 0; w[i] != 0; i++) {
    if (isupper(w[i]))
      w[i] = tolower(w[i]);
  }

  while (1) {
    cin >> t;
    if (s == "END_OF_TEXT")
      break;

    if (s.length() > W.length() + 1)
      continue;
    for (i = 0; w[i] != 0; i++) {
      if (isupper(t[i]))
        t[i] = tolower(t[i]);
    }
    if (s == w)
      count++;
  }
  cout << count << endl;
  return 0;
}

#include <cctype>
#include <iostream>
#include <string>
using namespace std;

int main() {
  string W;
  cin >> W;

  for (int i = 0; i < W.size(); i++) {
    W[i] = tolower(W[i]);
  }

  int cnt = 0;

  while (true) {
    string T;
    cin >> T;

    if (T == "END_OF_TEXT")
      break;

    for (int i = 0; i < T.size(); i++) {
      T[i] = tolower(T[i]);
    }

    if (T == W) {
      cnt++;
    }
  }

  cout << cnt << endl;

  return 0;
}


#include <iostream>
#include <string>
using namespace std;
int main() {
  string a;
  string b;
  int c = 0;
  cin >> a;
  while (true) {
    cin >> b;
    if (b == "END_OF_TEXT") {
      cout << c << endl;
      break;
    }
    for (int i = 0; i <= b.size() - a.size(); i++) {
      if (a == b.substr(i, a.size())) {
        c++;
      }
    }
  }
  return 0;
}

#include <cctype>
#include <iostream>
#include <string>
using namespace std;
int main() {
  string a;
  string b;
  string f;
  string g;
  int c = 0, d, e;
  cin >> a;
  d = a.size();
  while (true) {
    b = "";
    cin >> f;
    e = f.size();
    if (f == "END_OF_TEXT") {
      cout << c << endl;
      break;
    }
    for (int i = 0; i < e; i++) {
      b += tolower(f[i]);
    }
    if (b == a) {
      c++;
    }
  }
  return 0;
}

terminate called after throwing an instance of 'std::out_of_range'
  what():  basic_string::substr: __pos (which is 4) > this->size() (which is 3)


#include <algorithm>
#include <iostream>
#include <string>

using namespace std;

int main() {
  string W, str;
  int cnt = 0;
  cin >> W;
  transform(W.begin(), W.end(), W.begin(), ::tolower);
  do {
    cin >> str;
    transform(str.begin(), str.end(), str.begin(), ::tolower);
    if (W == str)
      cnt++;
  } while (str != "END_OF_TEXT");
  cout << cnt << endl;
}

#include <algorithm>
#include <cctype>
#include <climits>
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <string>

using namespace std;

int main() {
  string W, str;
  int cnt = 0;
  cin >> W;
  transform(W.begin(), W.end(), W.begin(), ::tolower);
  while (cin >> str) {
    if (str == "END_OF_TEXT")
      break;
    transform(str.begin(), str.end(), str.begin(), ::tolower);
    if (W == str)
      cnt++;
  }
  cout << cnt << endl;
}


#include <iostream>
#include <string>

int BruteForceSearch(std::string w, std::string t) {
  int i = 0, j = 0, n = 0;
  while (i < t.length()) {
    while (j < w.length()) {
      if (t[i] == w[j]) {
        i++;
        j++;
      } else {
        i = i - j + 1;
        j = 0;
      }
    }
    if (j == w.length())
      n++;
  }
  return n;
}

int main(void) {
  std::string t, w;
  std::getline(std::cin, w);
  int n = 0;
  while (1) {
    std::getline(std::cin, t);
    if (t == "END_OF_TEXT")
      break;
    n += BruteForceSearch(w, t);
  }
  std::cout << n << std::endl;
  return 0;
}

#include <algorithm>
#include <iostream>
#include <string>

int main() {
  int count = 0;
  std::string W;
  std::cin >> W;
  std::string str;
  while (1) {
    std::cin >> str;
    if (str == "END_OF_TEXT")
      break;
    std::transform(str.begin(), str.end(), str.begin(), tolower);
    if (str == W) {
      count++;
    }
  }
  std::cout << count << std::endl;
  return 0;
}


#include <bits/stdc++.h>
using namespace std;
int main(void) {
  string y;
  int a = 0;
  cin >> y;
  string x;
  while (true) {
    cin >> x;
    for (int i = 0; i < x.size(); i++) {
      x.at(i) = (char)tolower(x.at(i));
    }
    if (x == "END_OF_TEXT")
      break;
    if (x == y)
      a++;
  }
  cout << a << endl;
  return 0;
}

#include <bits/stdc++.h>
using namespace std;
int main(void) {
  string y;
  int a = 0;
  cin >> y;
  string x;
  while (true) {
    cin >> x;
    if (x == "END_OF_TEXT")
      break;
    for (int i = 0; i < x.size(); i++) {
      x.at(i) = (char)tolower(x.at(i));
    }

    if (x == y)
      a++;
  }
  cout << a << endl;
  return 0;
}


#include <iostream>
#include <string>

using namespace std;

int main() {
  int count = 0;
  string W, word;
  while (1) {
    cin >> W;
    if (W == "END_OF_TEXT")
      break;
    cin >> word;
    if (word == W)
      count++;
  }
  cout << count << endl;
  return 0;
}

#include <iostream>
#include <string>

using namespace std;

int main() {
  int count = 0;
  string W, word;
  cin >> W;
  int size = W.size();
  for (int i = 0; i < size; i++) {
    W[i] = toupper(W[i]);
  }
  while (1) {
    cin >> word;
    if (word == "END_OF_TEXT")
      break;
    int size = word.size();
    for (int i = 0; i < size; i++) {
      word[i] = toupper(word[i]);
    }
    if (word == W)
      count++;
  }
  cout << count << endl;
  return 0;
}


#include <iostream>
#include <string>
using namespace std;
int main() {
  int cnt = 0, match = 0;
  string w, t[150];
  cin >> w;
  string tt;
  int a = 0;
  while (cin >> tt, tt != "END_OF_TEXT") {
    t[a] = tt;
    a++;
  }
  for (int j = 0; j < a; j++) {
    for (int i = 0; i < t[j].size(); i++) {
      if (w.size() != t[j].size()) {
        break;
      }
      if (32 + w[match] == t[j][i] || w[match] == t[j][i] ||
          w[match] == t[j][i] + 32) {
        match++;
      } else {
        match = 0;
      }
      if (match == w.size()) {
        cnt++;
        match = 0;
      }
    }
    match = 0;
  }
  cout << cnt << endl;
  return 0;
}

#include <iostream>
#include <string>
using namespace std;
int main() {
  int cnt = 0, match = 0;
  string w, t[300];
  cin >> w;
  string tt;
  int a = 0;
  while (cin >> tt, tt != "END_OF_TEXT") {
    t[a] = tt;
    a++;
  }
  for (int j = 0; j < a; j++) {
    for (int i = 0; i < t[j].size(); i++) {
      if (w.size() != t[j].size()) {
        break;
      }
      if (32 + w[match] == t[j][i] || w[match] == t[j][i] ||
          w[match] == t[j][i] + 32) {
        match++;
      } else {
        match = 0;
      }
      if (match == w.size()) {
        cnt++;
        match = 0;
      }
    }
    match = 0;
  }
  cout << cnt << endl;
  return 0;
}


#include <iostream>
#include <string.h>
using namespace std;
int main() {
  string W, T;
  int a = 0;
  cin >> W;
  cin >> T;
  while (T != "END_OF_TEXT") {
    for (int i = 0; i < T.size(); i++) {
      if ('A' <= T[i] && T[i] <= 'Z') {
        T[i] += 'A' - 'a';
      }
      if (W == T) {
        a++;
      }
    }
  }
  cout << a << endl;
}

#include <iostream>
#include <string>
using namespace std;
int main() {
  string W, T;
  int i, a = 0;
  cin >> W;
  cin >> T;
  while (T != "END_OF_TEXT") {
    for (i = 0; i < T.size(); i++) {
      if ('A' <= T[i] && T[i] <= 'Z') {
        T[i] += 'a' - 'A';
      }
    }
    if (W == T) {
      a++;
    }
    cin >> T;
  }
  cout << a << endl;
  return 0;
}


#include <cstdio>

int main() {
  char word[12];
  char eot[12] = "end_of_text";
  int i = 1;
  int count = 0;
  char c;
  bool b;

  word[0] = ' ';

  scanf("%c", &c);
  while (c - 10) {
    if (64 < c && c < 91) {
      word[i] = c + 32;
    } else {
      word[i] = c;
    }
    i++;
    scanf("%c", &c);
  }
  word[i] = ' ';
  i++;

  int j = 0, k = 0;

  c = ' ';

  while (true) {
    if (64 < c && c < 91) {
      c += 32;
      b = false;
    } else if (c == '\\n') {
      c = ' ';
      b = true;
    }

    if (word[j] == c) {
      j++;
      if (j == i) {
        count++;
        j = 1;
      }
    } else {
      j = 0;
    }

    if (eot[k] == c) {
      k++;
      if (k == 11 && b) {
        printf("%d\\n", count);
        return 0;
      }
    } else {
      k = 0;
    }
    scanf("%c", &c);
  }
  return 0;
}


#include <iostream>
#include <string>
using namespace std;
int main() {
  int ans = 0;
  string w;
  cin >> w;
  for (int i = 0; i < w.size(); i++) {
    if (isupper(w[i])) {
      w[i] = tolower(w[i]);
    }
  }
  string t;
  while (true) {
    cin >> t;
    if (t == "END???OF???TEXT")
      break;

    for (int j = 0; j < t.size(); j++) {
      if (isupper(t[j])) {
        t[j] = tolower(t[j]);
      }
    }

    if (t == w) {
      ans++;
    }
  }

  cout << ans << endl;
  return 0;
}

#include <iostream>
#include <string>
using namespace std;
int main() {
  int ans = 0;
  string w;
  cin >> w;
  for (int i = 0; i < w.size(); i++) {
    if (isupper(w[i])) {
      w[i] = tolower(w[i]);
    }
  }
  string t;
  while (true) {
    cin >> t;
    if (t == "END_OF_TEXT")
      break;

    for (int j = 0; j < t.size(); j++) {
      if (isupper(t[j])) {
        t[j] = tolower(t[j]);
      }
    }

    if (t == w) {
      ans++;
    }
  }

  cout << ans << endl;
  return 0;
}


#include <algorithm>
#include <iostream>
#include <string>
using namespace std;

int main() {
  int count = 0;
  string W, T;
  cin >> W;
  string b = W;
  transform(b.begin(), b.end(), W.begin(), ::tolower);
  while (1) {
    cin >> T;
    string a = T;
    transform(a.begin(), a.end(), T.begin(), ::tolower);
    if (T.find(W) != string::npos)
      count++;
    if (T.find("END_OF_TEXT") != string::npos)
      break;
  }
  cout << count << endl;
  return 0;
}

#include <algorithm>
#include <iostream>
using namespace std;

int main() {
  int count = 0;
  string W, T;
  cin >> W;
  transform(W.begin(), W.end(), W.begin(), ::tolower);
  while (1) {
    cin >> T;
    if (T.find("END_OF_TEXT") != string::npos)
      break;
    transform(T.begin(), T.end(), T.begin(), ::tolower);
    if (T == W)
      count++;
  }
  cout << count << endl;
  return 0;
}


#include <cctype>
#include <iostream>
using namespace std;

#include <cstdio>
#include <cstring>
#include <string>

string Upper(string s) {
  string t = s;
  for (int i = 0; s.size(); i++) {
    t[i] = toupper(s[i]);
  }
  return t;
}

int main() {
  string W, T;
  int count = 0;

  cin >> W;
  W = Upper(W);

  while (1) {
    cin >> T;

    if (T == "END_OF_TEXT")
      break;
    T = Upper(T);
    if (T == W)
      count++;
  }

  cout << count << "\\n";

  return 0;
}

#include <cctype>
#include <iostream>
using namespace std;

#include <cstdio>
#include <cstring>
#include <string>

string Upper(string s) {
  string t = s;
  for (int i = 0; i < s.size(); i++) {
    t[i] = toupper(s[i]);
  }
  return t;
}

int main() {
  string W, T;
  int count = 0;

  cin >> W;
  W = Upper(W);

  while (1) {
    cin >> T;

    if (T == "END_OF_TEXT")
      break;
    T = Upper(T);
    if (T == W)
      count++;
  }

  cout << count << "\\n";

  return 0;
}


#include <iostream>
#include <string>
using namespace std;
void tim(string &a) {

  for (int i = 0; a[i]; i++) {
    if (a[i] < 97) {
      a[i] = (char)(a[i] + 32);
    }
  }
}
int main() {
  string w, t;
  cin >> w;
  int n = w.size();
  tim(w);
  getline(cin, t);
  int d = 0;
  while (t != "END_OF_TEXT") {
    tim(t);
    int x = 0;
    while (x != -1) {
      x = t.find(w);
      if (x != -1 && t[x - 1] == 20 && t[x + n] == 20) {
        d++;
        t.replace(x, n, "");
      }
    }
    getline(cin, t);
  }
  cout << d << endl;
  return 0;
}

#include <iostream>
#include <string>
using namespace std;
void tim(string &a) {

  for (int i = 0; a[i]; i++) {
    if (a[i] < 97) {
      a[i] = (char)(a[i] + 32);
    }
  }
}

int main() {
  string w, t;
  cin >> w;
  tim(w);
  cin >> t;
  int d = 0;
  while (t != "END_OF_TEXT") {
    tim(t);
    if (w == t)
      d++;
    cin >> t;
  }
  cout << d << endl;
  return 0;
}


#include <cctype>
#include <stdio.h>

int main(void) {

  char word[11] = "0000000000";
  char t[11] = "0000000000";
  char te[11] = "0000000000";
  char end[] = "END_OF_TEXT";
  int i = 0;
  int j = 0;
  int ans = 0;
  int count = 0;
  int endcount = 1;

  while (1) {
    scanf("%c", &word[i]);

    if (word[i] == '\\n') {
      break;
    }

    if (islower(word[i])) {
      word[i] = toupper(word[i]);
    }

    i++;
  }

  while (1) {
    while (1) {
      scanf("%c", &t[j]);
      te[j] = t[j];

      if (islower(t[j])) {
        t[j] = toupper(t[j]);
      }

      if (t[j] == '\\n' || t[j] == ' ') {
        break;
      }

      if (t[j] == word[j]) {
        ans++;
      } else {
        ans = 0;
      }

      j++;
    }

    if (ans == i && (t[j] == '\\n' || t[j] == ' ')) {
      count++;
    }

    ans = 0;

    for (j = 0; j < 11; j++) {
      if (te[j] != end[j]) {
        endcount = 0;
      }
    }

    if (endcount == 1) {
      break;
    }

    endcount = 1;
    j = 0;
  }

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

  return 0;
}

#include <cctype>
#include <stdio.h>

int main(void) {

  char word[11] = "0000000000";
  char t[11] = "0000000000";
  char end[] = "END_OF_TEXT";
  int i = 0;
  int j = 0;
  int k;
  int ans = 0;
  int count = 0;
  int endcount = 1;

  while (1) {
    scanf("%c", &word[i]);

    if (word[i] == '\\n') {
      break;
    }

    if (islower(word[i])) {
      word[i] = toupper(word[i]);
    }

    i++;
  }

  while (1) {
    while (1) {
      scanf("%c", &t[j]);

      if (t[j] == '\\n' || t[j] == ' ') {
        break;
      }

      j++;
    }

    for (k = 0; k < 11; k++) {
      if (t[k] != end[k]) {
        endcount = 0;
      }
    }

    if (endcount == 1) {
      break;
    }

    endcount = 1;

    for (k = 0; k < j; k++) {
      if (islower(t[k])) {
        t[k] = toupper(t[k]);
      }

      if (t[k] == word[k]) {
        ans++;
      } else {
        ans = 0;
      }
    }

    if (ans == i && (t[j] == '\\n' || t[j] == ' ')) {
      count++;
    }

    ans = 0;

    j = 0;
  }

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

  return 0;
}


#include <iostream>
#include <string>
using namespace std;

int main() {
  int count = 0;
  string w, str;

  cin >> w >> str;

  while (str != "END_OF_TEXT") {
    if (str == w)
      count++;
  }

  cout << count << endl;

  return 0;
}

#include <cctype>
#include <iostream>
#include <string>
using namespace std;

string toUpper(string s) {
  string t = s;
  for (int i = 0; i < s.size(); i++) {
    t[i] = toupper(s[i]);
  }

  return t;
}

int main() {
  int count = 0;
  string w, str;

  cin >> w >> str;
  w = toUpper(w);
  str = toUpper(str);

  while (1) {
    if (str == w)
      count++;

    cin >> str;
    if (str == "END_OF_TEXT")
      break;
    str = toUpper(str);
  }

  cout << count << endl;

  return 0;
}


#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;

int main() {
  int count = 0;
  string w;
  string t;
  cin >> w;

  while (1) {
    cin >> t;
    std::transform(t.begin(), t.end(), t.begin(), ::tolower);
    if (w == t)
      count++;
    if (t == "END_OF_TEXT")
      break;
  }
  cout << count << endl;
}

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;

int main() {
  int count = 0;
  string w;
  string t;
  cin >> w;

  while (1) {
    cin >> t;
    if (t == "END_OF_TEXT")
      break;
    for (int i = 0; i < t.length(); i++) {
      t[i] = tolower(t[i]);
    }
    if (w == t) {
      count++;
    }
  }
  cout << count << endl;
}


#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;

int main() {
  string w, t;
  int i;

  getline(cin, w);
  std::transform(w.begin(), w.end(), w.begin(), ::tolower);
  i = 0;

  getline(cin, t);
  std::transform(t.begin(), t.end(), t.begin(), ::tolower);

  while (t != "END_OF_TEXT") {
    if (t.find(w) != string::npos) {
      i++;
    }
    getline(cin, t);
    std::transform(t.begin(), t.end(), t.begin(), ::tolower);
  }
  cout << i << endl;
  return 0;
}

#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

string uniteBlanks(string t) {
  int i = 0;
  for (i = t.size() - 1; i > 0; i--) {
    if (t[i - 1] == ' ' && t[i] == ' ')
      t.erase(t.begin() + i);
  }
  if (t[0] == ' ')
    t.erase(t.begin());
  return t;
}

int main() {
  string w, t, t_tmp;
  int i, j, count, ix;
  vector<string> list;
  string item;

  getline(cin, w);
  std::transform(w.begin(), w.end(), w.begin(), ::toupper);
  count = 0;

  getline(cin, t_tmp);
  t = uniteBlanks(t_tmp);

  while (t != "END_OF_TEXT") {
    std::transform(t.begin(), t.end(), t.begin(), ::toupper);

    stringstream ss(t);
    while (getline(ss, item, ' ') && !item.empty()) {
      list.push_back(item);
    }

    for (i = 0; i != list.size(); i++) {
      if (w == list[i]) {
        count++;
      }
    }

    list.clear();
    getline(cin, t_tmp);
    t = uniteBlanks(t_tmp);
  }
  cout << count << endl;
  return 0;
}


#include <cctype>
#include <iostream>
#include <string>
using namespace std;
int main(void) {
  string W, H;
  int c = 0;
  cin >> W;
  for (int i = 1; i > 0; i++) {
    cin >> H;
    for (int j = 1; j <= H.size(); j++) {
      H[j] = tolower(H[j]);
    }
    if (H == W)
      c++;
    if (H == "END_OF_TEXT")
      break;
  }
  cout << c << endl;
  return 0;
}

#include <iostream>
#include <string>
using namespace std;
int main(void) {
  string W, H;
  int c = 0;
  cin >> W;
  while (cin >> H) {
    for (int j = 0; j < H.size(); j++) {
      if (isupper(H[j]))
        H[j] = tolower(H[j]);
    }
    if (H == W)
      c++;
    if (H == "END_OF_TEXT")
      break;
  }
  cout << c << endl;
  return 0;
}


//
//  main.cpp
//  ITP1-9-A
//
//  Created by h3037153 on 2017/08/28.
//  Copyright ?? 2017??´ h3037153. All rights reserved.
//
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
int main() {
  string W, T[100];
  int i = 0, counter = 0, ii = 0, len;
  char ch = {};
  cin >> W;
  while (cin >> T[i]) {
    if (T[i] == "END_OF_TEXT") {
      break;
    }
    len = T[i].length();
    for (ii = 0; ii < len; ii++) {
      if (isupper(T[i][ii])) {
        ch = tolower(T[i][ii]);
        T[i][ii] = ch;
      }
    }
    if (T[i] == W) {
      counter++;
    } else
      i++;
  }
  cout << counter << endl;
  return 0;
}

//
//  main.cpp
//  ITP1-9-A
//
//  Created by h3037153 on 2017/08/28.
//  Copyright ?? 2017??´ h3037153. All rights reserved.
//
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
int main() {
  string W, T[1000];
  int i = 0, counter = 0, ii = 0, len;
  char ch = {};
  cin >> W;
  while (cin >> T[i]) {
    if (T[i] == "END_OF_TEXT") {
      break;
    }
    len = T[i].length();
    for (ii = 0; ii < len; ii++) {
      if (isupper(T[i][ii])) {
        ch = tolower(T[i][ii]);
        T[i][ii] = ch;
      }
    }
    if (T[i] == W) {
      counter++;
    } else
      i++;
  }
  cout << counter << endl;
  return 0;
}


#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
int main() {
  string n, n2;
  cin >> n;

  int count = 0;

  while (n2 != "END_OF_TEXT") {
    cin >> n2;

    for (int i = 0; i < n2.size(); i++) {
      if (65 <= n2[i] && n2[i] <= 90) {
        n2[i] += 32;
      }
    }

    if (n == n2) {
      count++;
    }
  }

  cout << count << endl;
}

#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
int main() {
  string n, n2;
  cin >> n;

  int count = 0;

  while (1) {
    cin >> n2;

    if (n2 == "END_OF_TEXT")
      break;

    for (int i = 0; i < n2.size(); i++) {
      if (65 <= n2[i] && n2[i] <= 90) {
        n2[i] += 32;
      }
    }

    if (n == n2) {
      count++;
    }
  }

  cout << count << endl;
}


#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
const int INF = 1001001001;
const int mod = 1000000007;
struct UnionFind {
  vector<int> d;
  UnionFind(int n = 0) : d(n, -1) {}
  int root(int x) {
    if (d[x] < 0)
      return x;
    return d[x] = root(d[x]);
  }
  void unite(int x, int y) {
    int root_x = root(x);
    int root_y = root(y);
    if (root_x == root_y)
      return;
    if (d[root_x] > d[root_y])
      swap(root_x, root_y);
    d[root_x] += d[root_y];
    d[root_y] = root_x;
  }
  bool same(int x, int y) { return root(x) == root(y); }
  int size(int x) { return -d[root(x)]; }
};

int main() {
  int n, m;
  cin >> n >> m;
  UnionFind uf(n);
  rep(i, m) {
    int a, b;
    a--;
    b--;
    cin >> a >> b;
    uf.unite(a, b);
  }
  int ans = 0;
  rep(i, n) if (uf.d.at(i) < 0) ans++;
  ans = max(ans - 1, 0);
  cout << ans << endl;
}

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
const int INF = 1001001001;
const int mod = 1000000007;
struct UnionFind {
  vector<int> d;
  UnionFind(int n = 0) : d(n, -1) {}
  int root(int x) {
    if (d[x] < 0)
      return x;
    return d[x] = root(d[x]);
  }
  void unite(int x, int y) {
    int root_x = root(x);
    int root_y = root(y);
    if (root_x == root_y)
      return;
    if (d[root_x] > d[root_y])
      swap(root_x, root_y);
    d[root_x] += d[root_y];
    d[root_y] = root_x;
  }
  bool same(int x, int y) { return root(x) == root(y); }
  int size(int x) { return -d[root(x)]; }
};

int main() {
  int n, m;
  cin >> n >> m;
  UnionFind uf(n);
  rep(i, m) {
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    uf.unite(a, b);
  }
  int ans = 0;
  rep(i, n) if (uf.d.at(i) < 0) ans++;
  ans = max(ans - 1, 0);
  cout << ans << endl;
}


#include <cstdio>
#include <iostream>

using namespace std;

const int N = 1e6 + 10;
int p[N];
int n, m;
int find(int x) {
  if (p[x] != x)
    p[x] = find(p[x]);
  else
    return x;
}
int main() {
  scanf("%d%d", &n, &m);

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

  for (int i = 0; i < m; i++) {
    int x, y;
    scanf("%d%d", &x, &y);
    p[find(x)] = find(y);
  }
  int ans = 0;
  for (int i = 1; i <= n; i++) {
    if (p[i] != i)
      ans++;
  }
  cout << n - ans - 1;

  return 0;
}

#include <cstdio>
#include <iostream>

using namespace std;

const int maxn = 10e6 + 10;
int p[maxn];
int s[maxn];
int vis[maxn] = {0};
int n, m;
int find(int x) {
  if (p[x] != x)
    p[x] = find(p[x]);
  return p[x];
}

int main() {
  scanf("%d%d", &n, &m);

  for (int i = 1; i <= n; i++) {
    p[i] = i;
    s[i] = 1;
  }

  for (int i = 0; i < m; i++) {
    int x, y;
    scanf("%d%d", &x, &y);
    x = find(x);
    y = find(y);
    if (x == y)
      continue;
    p[y] = x;
    s[x] += s[y];
  }

  int ans = 0;
  for (int i = 1; i <= n - 1; i++) {
    int pa = find(i);
    int pb = find(i + 1);
    if (pa != pb) {
      p[pa] = pb;
      ans++;
    }
  }
  cout << ans;

  return 0;
}


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

int main() {
  int n, m, c = 0;
  cin >> n >> m;
  vector<set<int>> vs;
  for (int i = 0; i < m; i++) {
    int x, y, f = 1;
    cin >> x >> y;
    for (int j = 0; j < vs.size(); j++) {
      if (vs[i].end() != vs[i].find(x) || vs[i].end() != vs[i].find(y)) {
        vs[i].insert(x);
        vs[i].insert(y);
        f = 0;
      }
    }
    if (f) {
      set<int> s;
      s.insert(x);
      s.insert(y);
      vs.push_back(s);
    }
  }
  for (int j = 0; j < vs.size(); j++) {
    c += vs[j].size();
  }
  cout << vs.size() + (n - c) - 1;
  return 0;
}

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

vector<int> edges[100001];
int n, m;
bool *visited = new bool[100001]();

void dfs(int ind) {

  visited[ind] = true;
  for (int i = 0; i < edges[ind].size(); i++) {
    if (!visited[edges[ind][i]]) {
      dfs(edges[ind][i]);
    }
  }
}

int main() {
  // your code goes here

  cin >> n >> m;

  for (int i = 0; i < m; i++) {
    int f, s;
    cin >> f >> s;
    edges[f].push_back(s);
    edges[s].push_back(f);
  }
  int cnt = 0;
  for (int i = 1; i <= n; i++) {
    if (!visited[i]) {
      cnt++;
      dfs(i);
    }
  }
  cout << cnt - 1;
  return 0;
}


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

int root(int x, vector<int> &city) {
  if (city.at(x) == x) {
    return x;
  } else {
    return city.at(x) = root(city.at(x), city);
  }
}

int main() {
  int n, m;
  cin >> n >> m;
  vector<int> city(n);
  vector<int> rank(n);
  vector<int> unionCity(n);
  for (int i = 0; i < n; i++) {
    city.at(i) = i;
    rank.at(i) = 0;
    unionCity.at(i) = 0;
  }

  for (int i = 0; i < m; i++) {
    int a, b;
    cin >> a >> b;
    int x = city.at(a - 1);
    int y = city.at(b - 1);
    if (rank[x] < rank[y]) {
      city.at(x) = y;
    } else {
      city.at(y) = x;
    }
    if (rank.at(x) == rank.at(y)) {
      rank.at(x)++;
    }
  }

  for (int i = 0; i < n; i++) {
    int rt = root(i, city);
    if (unionCity.at(rt) == 0) {
      unionCity.at(rt)++;
    }
  }
  int sum = 0;

  for (int i = 0; i < n; i++) {
    sum += unionCity.at(i);
  }
  cout << sum - 1 << endl;
}

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

int root(int x, vector<int> &city) {
  if (city.at(x) == x) {
    return x;
  } else {
    return city.at(x) = root(city.at(x), city);
  }
}

int main() {
  int n, m;
  cin >> n >> m;
  vector<int> city(n);
  vector<int> rank(n);
  vector<int> unionCity(n);
  for (int i = 0; i < n; i++) {
    city.at(i) = i;
    rank.at(i) = 0;
    unionCity.at(i) = 0;
  }

  for (int i = 0; i < m; i++) {
    int a, b;
    cin >> a >> b;
    int x = city.at(a - 1);
    int y = city.at(b - 1);
    int rtx = root(x, city);
    int rty = root(y, city);
    if (rtx != rty) {
      if (rank[rtx] < rank[rty]) {
        city.at(rtx) = rty;
      } else {
        city.at(rty) = rtx;
      }
      if (rank.at(rtx) == rank.at(rty)) {
        rank.at(rtx)++;
      }
    }
  }

  for (int i = 0; i < n; i++) {
    int rt = root(i, city);
    if (unionCity.at(rt) == 0) {
      unionCity.at(rt)++;
    }
  }
  int sum = 0;

  for (int i = 0; i < n; i++) {
    sum += unionCity.at(i);
  }
  cout << sum - 1 << endl;
}


// atcoder
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
vector<int> v[10003];
bool used[10003];
void dfs(int root) {
  // if(used[root]==true) return;
  //	used[root]=true;
  for (auto x : v[root]) {
    if (used[x] == false)
      dfs(x);
  }
  return;
}
int main() {
  int n, m, u, w;

  cin >> n >> m;
  while (m > 0) {
    cin >> u >> w;
    v[u].push_back(w);
    v[w].push_back(u);
    m--;
  }

  int road = 0;
  memset(used, false, sizeof(used));
  for (int i = 1; i <= n; i++) {
    if (!used[i]) {
      dfs(i);
      road++;
    }
  }

  cout << road - 1;

  return 0;
}

// atcoder
#include <bits/stdc++.h>
using namespace std;
typedef long long int ll;
vector<int> v[100003];
bool used[100003];
void dfs(int root) {
  //	if(used[root]==true) return;
  used[root] = true;
  for (auto x : v[root]) {
    if (used[x] == false)
      dfs(x);
  }
  return;
}
int main() {
  int n, m, u, w;

  cin >> n >> m;
  while (m > 0) {
    cin >> u >> w;
    v[u].push_back(w);
    v[w].push_back(u);
    m--;
  }

  int road = 0;
  memset(used, false, sizeof(used));
  for (int i = 1; i <= n; i++) {
    if (!used[i]) {
      dfs(i);
      road++;
    }
  }

  cout << road - 1;

  return 0;
}


#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define REP(a, b) for (int a = 0; a < (b); ++a)
#define REP1(i, n) for (int i = 1; i <= (n); ++i)
#define debug(x) cerr << #x << ": " << x << '\\n'
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define YES() printf("YES\\n")
#define NO() printf("NO\\n")
#define isYES(x) printf("%s\\n", (x) ? "YES" : "NO")
#define Yes() printf("Yes\\n")
#define No() printf("No\\n")
#define isYes(x) printf("%s\\n", (x) ? "Yes" : "No")
#define isPossible(x) printf("%s\\n", (x) ? "Possible" : "Impossible")
#define SZ(x) ((int)(x).size())
#define pb push_back
#define mp make_pair
// #define INF (1<<29)

const long long INF = 1LL << 50;

#define Sp(p) cout << setprecision(25) << fixed << p << endl
#define vi vector<int>
#define vl vector<ll>
#define vii vector<vector<int>>
#define vll vector<vector<ll>>
#define vs vector<string>
#define pii pair<int, int>
#define pis pair<int, string>
#define psi pair<string, int>
#define pll pair<ll, ll>
#define pie 3.14159265358979323846
using namespace std;
typedef pair<int, int> P;
typedef pair<ll, ll> LP;
typedef pair<int, P> PP;
typedef pair<ll, LP> LPP;
template <class T = int> T in() {
  T x;
  cin >> x;
  return (x);
}
template <class T> void print(T &x) { cout << x << '\\n'; }

const int MOD = (int)1e9 + 7;
const int mod = (int)998244353;
// const int mod =(int)1e9+7;

const int MAX = 510000;
ll fac[MAX], finv[MAX], inv[MAX];
void COMint() {
  fac[0] = fac[1] = 1;
  finv[0] = finv[1] = 1;
  inv[1] = 1;
  for (int i = 2; i < MAX; i++) {
    fac[i] = fac[i - 1] * i % MOD;
    inv[i] = MOD - inv[MOD % i] * (MOD / i) % MOD;
    finv[i] = finv[i - 1] * inv[i] % MOD;
  }
}
ll COM(int n, int k) {
  if (n < k)
    return 0;
  if (n < 0 || k < 0)
    return 0;
  return fac[n] * (finv[k] * finv[n - k] % MOD) % MOD;
}
ll gcd(ll a, ll b) {
  if (a < 0)
    a = -a;
  if (b < 0)
    b = -b;
  if (b == 0)
    return a;
  if (a > b) {
    swap(a, b);
  }
  return gcd(a, b % a);
}
ll lcm(ll a, ll b) {
  if (a < 0)
    a = -a;
  if (b < 0)
    b = -b;
  ll g;
  g = gcd(a, b);
  return b / g * a;
}
bool compare_by_b(pair<int, int> a, pair<int, int> b) {
  if (a.second != b.second) {
    return a.second < b.second;
  } else {
    return a.first < b.first;
  }
}
bool compare_by_a(pair<int, int> a, pair<int, int> b) {
  if (a.first != b.first) {
    return a.first < b.first;
  } else {
    return a.second < b.second;
  }
}
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;
}
// int N, M, K, T, H, W, L, R;
long long int N, M, K, T, H, W, L, R;
struct mint {
  ll x; // typedef long long ll;
  mint(ll x = 0) : x((x % mod + mod) % mod) {}
  mint &operator+=(const mint a) {
    if ((x += a.x) >= mod)
      x -= mod;
    return *this;
  }
  mint &operator-=(const mint a) {
    if ((x += mod - a.x) >= mod)
      x -= mod;
    return *this;
  }
  mint &operator*=(const mint a) {
    (x *= a.x) %= mod;
    return *this;
  }
  mint operator+(const mint a) const {
    mint res(*this);
    return res += a;
  }
  mint operator-(const mint a) const {
    mint res(*this);
    return res -= a;
  }
  mint operator*(const mint a) const {
    mint res(*this);
    return res *= a;
  }
  mint pow(ll t) const {
    if (!t)
      return 1;
    mint a = pow(t >> 1);
    a *= a;
    if (t & 1)
      a *= *this;
    return a;
  }
  mint inv() const { return pow(mod - 2); }
  mint &operator/=(const mint a) { return (*this) *= a.inv(); }
  mint operator/(const mint a) const {
    mint res(*this);
    return res /= a;
  }
};
istream &operator>>(istream &is, mint &a) { return is >> a.x; }
ostream &operator<<(ostream &os, const mint &a) { return os << a.x; }
struct combination {
  vector<mint> fact, ifact;
  combination(int n) : fact(n + 1), ifact(n + 1) {
    assert(n < mod);
    fact[0] = 1;
    for (int i = 1; i <= n; ++i)
      fact[i] = fact[i - 1] * i;
    ifact[n] = fact[n].inv();
    for (int i = n; i >= 1; --i)
      ifact[i - 1] = ifact[i] * i;
  }
  mint operator()(int n, int k) {
    if (k < 0 || k > n)
      return 0;
    return fact[n] * ifact[k] * ifact[n - k];
  }
} c(2000005);
struct UnionFind {
  vi d;
  UnionFind(int n) : d(n, -1){};
  int deb(int x) { return d[x]; }
  int root(int x) {
    if (d[x] < 0)
      return x;
    return d[x] = root(d[x]);
  }
  int unite(int x, int y) {
    x = root(x);
    y = root(y);
    if (d[x] > d[y])
      swap(x, y);
    d[x] += d[y];
    d[y] = x;
    return true;
  }
  bool same(int x, int y) { return root(x) == root(y); }
  int size(int x) { return -d[root(x)]; }
};
int main() {
  ios::sync_with_stdio(false);
  int N, M;
  cin >> N >> M;
  UnionFind uf(N);
  REP(i, M) {
    int a, b;
    cin >> a >> b;
    --a;
    --b;
    uf.unite(a, b);
  }
  vi ans(N, 0);
  REP(i, N) { ans[uf.root(i)] = 1; }
  int cnt = 0;
  REP(i, N) { cnt += ans[i]; }
  cout << cnt - 1 << endl;
  return 0;
}

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define REP(a, b) for (int a = 0; a < (b); ++a)
#define REP1(i, n) for (int i = 1; i <= (n); ++i)
#define debug(x) cerr << #x << ": " << x << '\\n'
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define YES() printf("YES\\n")
#define NO() printf("NO\\n")
#define isYES(x) printf("%s\\n", (x) ? "YES" : "NO")
#define Yes() printf("Yes\\n")
#define No() printf("No\\n")
#define isYes(x) printf("%s\\n", (x) ? "Yes" : "No")
#define isPossible(x) printf("%s\\n", (x) ? "Possible" : "Impossible")
#define SZ(x) ((int)(x).size())
#define pb push_back
#define mp make_pair
// #define INF (1<<29)

const long long INF = 1LL << 50;

#define Sp(p) cout << setprecision(25) << fixed << p << endl
#define vi vector<int>
#define vl vector<ll>
#define vii vector<vector<int>>
#define vll vector<vector<ll>>
#define vs vector<string>
#define pii pair<int, int>
#define pis pair<int, string>
#define psi pair<string, int>
#define pll pair<ll, ll>
#define pie 3.14159265358979323846
using namespace std;
typedef pair<int, int> P;
typedef pair<ll, ll> LP;
typedef pair<int, P> PP;
typedef pair<ll, LP> LPP;
template <class T = int> T in() {
  T x;
  cin >> x;
  return (x);
}
template <class T> void print(T &x) { cout << x << '\\n'; }

const int MOD = (int)1e9 + 7;
const int mod = (int)998244353;
// const int mod =(int)1e9+7;

const int MAX = 510000;
ll fac[MAX], finv[MAX], inv[MAX];
void COMint() {
  fac[0] = fac[1] = 1;
  finv[0] = finv[1] = 1;
  inv[1] = 1;
  for (int i = 2; i < MAX; i++) {
    fac[i] = fac[i - 1] * i % MOD;
    inv[i] = MOD - inv[MOD % i] * (MOD / i) % MOD;
    finv[i] = finv[i - 1] * inv[i] % MOD;
  }
}
ll COM(int n, int k) {
  if (n < k)
    return 0;
  if (n < 0 || k < 0)
    return 0;
  return fac[n] * (finv[k] * finv[n - k] % MOD) % MOD;
}
ll gcd(ll a, ll b) {
  if (a < 0)
    a = -a;
  if (b < 0)
    b = -b;
  if (b == 0)
    return a;
  if (a > b) {
    swap(a, b);
  }
  return gcd(a, b % a);
}
ll lcm(ll a, ll b) {
  if (a < 0)
    a = -a;
  if (b < 0)
    b = -b;
  ll g;
  g = gcd(a, b);
  return b / g * a;
}
bool compare_by_b(pair<int, int> a, pair<int, int> b) {
  if (a.second != b.second) {
    return a.second < b.second;
  } else {
    return a.first < b.first;
  }
}
bool compare_by_a(pair<int, int> a, pair<int, int> b) {
  if (a.first != b.first) {
    return a.first < b.first;
  } else {
    return a.second < b.second;
  }
}
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;
}
// int N, M, K, T, H, W, L, R;
long long int N, M, K, T, H, W, L, R;
struct mint {
  ll x; // typedef long long ll;
  mint(ll x = 0) : x((x % mod + mod) % mod) {}
  mint &operator+=(const mint a) {
    if ((x += a.x) >= mod)
      x -= mod;
    return *this;
  }
  mint &operator-=(const mint a) {
    if ((x += mod - a.x) >= mod)
      x -= mod;
    return *this;
  }
  mint &operator*=(const mint a) {
    (x *= a.x) %= mod;
    return *this;
  }
  mint operator+(const mint a) const {
    mint res(*this);
    return res += a;
  }
  mint operator-(const mint a) const {
    mint res(*this);
    return res -= a;
  }
  mint operator*(const mint a) const {
    mint res(*this);
    return res *= a;
  }
  mint pow(ll t) const {
    if (!t)
      return 1;
    mint a = pow(t >> 1);
    a *= a;
    if (t & 1)
      a *= *this;
    return a;
  }
  mint inv() const { return pow(mod - 2); }
  mint &operator/=(const mint a) { return (*this) *= a.inv(); }
  mint operator/(const mint a) const {
    mint res(*this);
    return res /= a;
  }
};
istream &operator>>(istream &is, mint &a) { return is >> a.x; }
ostream &operator<<(ostream &os, const mint &a) { return os << a.x; }
struct combination {
  vector<mint> fact, ifact;
  combination(int n) : fact(n + 1), ifact(n + 1) {
    assert(n < mod);
    fact[0] = 1;
    for (int i = 1; i <= n; ++i)
      fact[i] = fact[i - 1] * i;
    ifact[n] = fact[n].inv();
    for (int i = n; i >= 1; --i)
      ifact[i - 1] = ifact[i] * i;
  }
  mint operator()(int n, int k) {
    if (k < 0 || k > n)
      return 0;
    return fact[n] * ifact[k] * ifact[n - k];
  }
} c(2000005);
struct UnionFind {
  vi d;
  UnionFind(int n) : d(n, -1){};
  int deb(int x) { return d[x]; }
  int root(int x) {
    if (d[x] < 0)
      return x;
    return d[x] = root(d[x]);
  }
  int unite(int x, int y) {
    x = root(x);
    y = root(y);
    if (d[x] > d[y])
      swap(x, y);
    d[x] += d[y];
    d[y] = x;
    return true;
  }
  bool same(int x, int y) { return root(x) == root(y); }
  int size(int x) { return -d[root(x)]; }
};
int main() {
  ios::sync_with_stdio(false);
  int N, M;
  cin >> N >> M;
  UnionFind uf(N);
  int cnt = 0;
  REP(i, M) {
    int a, b;
    cin >> a >> b;
    --a;
    --b;
    if (!uf.same(a, b)) {
      uf.unite(a, b);
      cnt++;
    }
  }
  cout << N - 1 - cnt << endl;
  return 0;
}


#pragma warning(disable : 4996)
#include <bits/stdc++.h>
#define all(x) (x).begin(), (x).end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef vector<ll> vll;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pld;
typedef tuple<ll, ll, ll> tl3;
#define FOR(a, b, c) for (int(a) = (b); (a) < (c); ++(a))
#define FORN(a, b, c) for (int(a) = (b); (a) <= (c); ++(a))
#define rep(i, n) FOR(i, 0, n)
#define repn(i, n) FORN(i, 1, n)
#define tc(t) while (t--)
// https://atcoder.jp/contests/abl/tasks/abl_c
vector<ll> v[101010];
char isVisited[101010] = {
    0,
};
ll cnt = 1;
void bfs(ll x) {
  queue<ll> q;
  q.push(x);
  isVisited[x] = cnt;
  while (!q.empty()) {
    ll cur = q.front();
    q.pop();
    for (int i = 0; i < v[cur].size(); i++) {
      if (!isVisited[v[cur][i]]) {
        isVisited[v[cur][i]] = cnt;
        q.push(v[cur][i]);
      }
    }
  }
  cnt++;
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  ll n, m;
  cin >> n >> m;
  rep(i, m) {
    ll a, b;
    cin >> a >> b;
    v[a].push_back(b);
    v[b].push_back(a);
  }
  for (int i = 1; i <= n; i++) {
    if (!isVisited[i])
      bfs(i);
  }
  cout << cnt - 2;

  return 0;
}

#pragma warning(disable : 4996)
#include <bits/stdc++.h>
#define all(x) (x).begin(), (x).end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef vector<ll> vll;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pld;
typedef tuple<ll, ll, ll> tl3;
#define FOR(a, b, c) for (int(a) = (b); (a) < (c); ++(a))
#define FORN(a, b, c) for (int(a) = (b); (a) <= (c); ++(a))
#define rep(i, n) FOR(i, 0, n)
#define repn(i, n) FORN(i, 1, n)
#define tc(t) while (t--)
// https://atcoder.jp/contests/abl/tasks/abl_c
vector<ll> v[101010];
bool isVisited[101010] = {
    0,
};
ll cnt = 0;
void dfs(ll x) {
  isVisited[x] = 1;
  for (auto k : v[x]) {
    if (isVisited[k] == 0) {
      dfs(k);
    }
  }
}
int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  ll n, m;
  cin >> n >> m;
  rep(i, m) {
    ll a, b;
    cin >> a >> b;
    v[a].push_back(b);
    v[b].push_back(a);
  }
  repn(i, n) {
    if (isVisited[i] == 0) {
      dfs(i);
      cnt++;
    }
  }
  cout << cnt - 1;

  return 0;
}


#include <bits/stdc++.h>
const int mmax = 1e5;
using namespace std;
int n, m, ans;
bool vis[mmax];
vector<int> adj[mmax];
void dfs(int u) {
  vis[u] = 1;
  for (int v : adj[u])
    if (!vis[v])
      dfs(v);
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  // freopen("test.inp", "r", stdin);
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);
  }
  for (int i = 1; i <= n; i++)
    if (!vis[i]) {
      dfs(i);
      ans++;
    }
  cout << --ans;
  return 0;
}

#include <bits/stdc++.h>
const int mmax = 2e6;
using namespace std;
long long n, m, ans;
bool vis[mmax];
vector<int> adj[mmax];
void dfs(int u) {
  vis[u] = 1;
  for (int v : adj[u])
    if (!vis[v])
      dfs(v);
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  // freopen("test.inp", "r", stdin);
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);
  }
  for (int i = 1; i <= n; i++)
    if (!vis[i]) {
      dfs(i);
      ans++;
    }
  cout << --ans;
  return 0;
}


#include <bits/stdc++.h>
const int mmax = 1e5;
using namespace std;
long long n, m, ans;
bool vis[mmax];
vector<int> adj[mmax];
void dfs(int u) {
  vis[u] = 1;
  for (int v : adj[u])
    if (!vis[v])
      dfs(v);
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  // freopen("test.inp", "r", stdin);
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);
  }
  for (int i = 1; i <= n; i++)
    if (!vis[i]) {
      dfs(i);
      ans++;
    }
  cout << --ans;
  return 0;
}

#include <bits/stdc++.h>
const int mmax = 1e6;
using namespace std;
long long n, m, ans;
bool vis[mmax];
vector<int> adj[mmax];
void dfs(int u) {
  vis[u] = 1;
  for (int v : adj[u])
    if (!vis[v])
      dfs(v);
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  // freopen("test.inp", "r", stdin);
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    int u, v;
    cin >> u >> v;
    adj[u].push_back(v);
    adj[v].push_back(u);
  }
  for (int i = 1; i <= n; i++)
    if (!vis[i]) {
      dfs(i);
      ans++;
    }
  cout << --ans;
  return 0;
}


#include <stdio.h>
#include <string.h>
const int N = 1e5 + 10;
int f[N];
int n, m;

int getf(int v) {
  if (f[v] != v)
    f[v] = getf(f[v]);
  return f[v];
}
int merge(int v, int u) {
  int t1, t2;
  t1 = getf(v);
  t2 = getf(u);

  if (t1 != t2) {
    f[t2] = t1;
  }
}
int main() {
  int T, i, j, sum, a, b;

  sum = 0;
  scanf("%d %d", &n, &m);

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

  for (i = 1; i <= m; i++) {
    scanf("%d %d", &a, &b);
    merge(a, b);
  }
  for (i = 1; i <= n; i++) {
    if (f[i] == i) {
      sum++;
    }
  }
  printf("%d\\n", sum - 1);

  return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 20;
int fa[N];
int xfind(int x) { return fa[x] == x ? x : fa[x] = xfind(fa[x]); }
void into(int x, int y) {
  int xx = xfind(x);
  int yy = xfind(y);
  if (xx != yy)
    fa[xx] = yy;
}
int main() {

  int n, m;
  cin >> n >> m;
  for (int i = 1; i <= N; i++)
    fa[i] = i;
  int x, y;
  for (int i = 1; i <= m; i++) {
    cin >> x >> y;
    into(x, y);
  }
  int cnt = 0;
  for (int i = 1; i <= n; i++) {
    if (fa[i] == i)
      cnt++;
  }
  cout << cnt - 1 << endl;
}


#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < n; i++)
#define repl(i, l, r) for (ll i = (l); i < (r); i++)
#define all(x) (x).begin(), (x).end()
using ll = long long;
const ll MOD = 1000000007;
struct UnionFind {
  vector<int> par;
  UnionFind(int n) : par(n, -1) {}
  void init(int n) { par.assign(n, -1); }
  int root(int x) {
    if (par[x] < 0)
      return x;
    return par[x] = root(par[x]);
  }
  bool issame(int x, int y) { return root(x) == root(y); }
  bool merge(int x, int y) {
    x = root(x), y = root(y);
    if (x == y)
      return false;
    if (par[x] > par[y])
      swap(x, y);
    par[x] += par[y];
    par[y] = x;
    return true;
  }
  int size(int x) { return -par[root(x)]; }
};
int main() {
  int n, q;
  scanf("%d%d", &n, &q);
  UnionFind tree(n);
  while (q--) {
    int a, b;
    a--, b--;
    scanf("%d%d", &a, &b);
    tree.merge(a, b);
  }
  int ans = 0;
  rep(i, n) {
    if (tree.par[i] < 0)
      ans++;
  }
  cout << ans - 1 << endl;
  return 0;
}

#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (ll i = 0; i < n; i++)
#define repl(i, l, r) for (ll i = (l); i < (r); i++)
#define all(x) (x).begin(), (x).end()
using ll = long long;
const ll MOD = 1000000007;
struct UnionFind {
  vector<int> par;
  UnionFind(int n) : par(n, -1) {}
  void init(int n) { par.assign(n, -1); }
  int root(int x) {
    if (par[x] < 0)
      return x;
    return par[x] = root(par[x]);
  }
  bool issame(int x, int y) { return root(x) == root(y); }
  bool merge(int x, int y) {
    x = root(x), y = root(y);
    if (x == y)
      return false;
    if (par[x] > par[y])
      swap(x, y);
    par[x] += par[y];
    par[y] = x;
    return true;
  }
  int size(int x) { return -par[root(x)]; }
};
int main() {
  int n, q;
  scanf("%d%d", &n, &q);
  UnionFind tree(n);
  while (q--) {
    int a, b;
    scanf("%d%d", &a, &b);
    a--, b--;
    tree.merge(a, b);
  }
  int ans = 0;
  rep(i, n) {
    if (tree.par[i] < 0)
      ans++;
  }
  cout << ans - 1 << endl;
  return 0;
}


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

void dfs(int node, vector<vector<int>> arr, char c[]) {
  if (c[node] == 'g') {
    return;
  }
  c[node] = 'g';
  for (int i = 0; i < arr[node].size(); i++) {
    if (c[arr[node][i]] == 'w') {
      dfs(arr[node][i], arr, c);
    }
  }
}

int main() {
  /*
   #ifndef ONLINE_JUDGE
// for getting input from input.txt
freopen("input.txt", "r", stdin);
//for writing output to output.txt
freopen("output.txt", "w", stdout);
#endif
*/

  int n, m;
  cin >> n >> m;
  vector<vector<int>> arr(n + 1);
  int a, b;
  for (int i = 0; i < m; i++) {
    cin >> a >> b;
    arr[a].push_back(b);
    arr[b].push_back(a);
  }
  char *c = new char[n + 1];
  for (int i = 0; i <= n; i++) {
    c[i] = 'w';
  }
  int ans = 0;
  for (int i = 1; i <= n; i++) {
    if (c[i] == 'w') {

      dfs(i, arr, c);
      if (i == 1) {
        continue;
      }
      ans++;
    }
  }
  cout << ans << endl;

  return 0;
}

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

void dfs(int node, vector<vector<int>> &arr, char c[]) {
  if (c[node] == 'g') {
    return;
  }
  c[node] = 'g';
  for (int i = 0; i < arr[node].size(); i++) {
    if (c[arr[node][i]] == 'w') {
      dfs(arr[node][i], arr, c);
    }
  }
}

int main() {
  /*
   #ifndef ONLINE_JUDGE
// for getting input from input.txt
freopen("input.txt", "r", stdin);
//for writing output to output.txt
freopen("output.txt", "w", stdout);
#endif
*/

  int n, m;
  cin >> n >> m;
  vector<vector<int>> arr(n + 1);
  int a, b;
  for (int i = 0; i < m; i++) {
    cin >> a >> b;
    arr[a].push_back(b);
    arr[b].push_back(a);
  }
  char *c = new char[n + 1];
  for (int i = 0; i <= n; i++) {
    c[i] = 'w';
  }
  int ans = 0;
  for (int i = 1; i <= n; i++) {
    if (c[i] == 'w') {

      dfs(i, arr, c);
      if (i == 1) {
        continue;
      }
      ans++;
    }
  }
  cout << ans << endl;

  return 0;
}


#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1000000007;
const long long INF = 9999999999999999;
using ll = long long;
struct UnionFind {
  vector<int> r;

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

  int root(int x) {
    if (r[x] < 0)
      return x;
    return r[x] = root(r[x]);
  }

  bool unite(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    if (r[x] > r[y])
      swap(x, y);
    r[x] += r[y];
    r[y] = x;
    return true;
  }

  int size(int x) { return -r[root(x)]; }
};

int main() {
  int N;
  cin >> N;
  UnionFind UF(N);
  int M;
  cin >> M;
  for (int i = 0; i < M; i++) {
    int a, b;
    a--;
    b--;
    cin >> a >> b;
    UF.unite(a, b);
  }
  int ans = 0;
  for (int i = 0; i < N; i++) {
    if (i == UF.root(i)) {
      ans++;
    }
  }
  cout << ans - 1 << endl;
  return 0;
}

#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1000000007;
const long long INF = 9999999999999999;
using ll = long long;
struct UnionFind {
  vector<int> r;

  UnionFind(int N) { r = vector<int>(N + 1, -1); }

  int root(int x) {
    if (r[x] < 0)
      return x;
    return r[x] = root(r[x]);
  }

  bool unite(int x, int y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    if (r[x] > r[y])
      swap(x, y);
    r[x] += r[y];
    r[y] = x;
    return true;
  }

  int size(int x) { return -r[root(x)]; }
};

int main() {
  int N;
  cin >> N;
  UnionFind UF(N);
  int M;
  cin >> M;
  for (int i = 0; i < M; i++) {
    int a, b;
    a;
    b;
    cin >> a >> b;
    UF.unite(a, b);
  }
  int ans = 0;
  for (int i = 1; i <= N; i++) {
    if (i == UF.root(i)) {
      ans++;
    }
  }
  cout << ans - 1 << endl;
  return 0;
}


#include <bits/stdc++.h>
#define ll long long int
#define mk make_pair
#define pb push_back
#define INF (ll)1e18
#define pii pair<ll, ll>
#define mod 1000000007 // 998244353
#define f(i, a, b) for (ll i = a; i < b; i++)
#define fb(i, a, b) for (ll i = a; i > b; i--)
#define ff first
#define ss second
#define srt(v)                                                                 \\
  if (!v.empty())                                                              \\
  sort(v.begin(), v.end())
#define PI 3.141592653589793238
#define pq priority_queue<ll>
#define pqr priority_queue<ll, vector<ll>, greater<ll>()>
using namespace std;
ll pow_mod(ll a, ll b) {
  ll res = 1;
  while (b != 0) {
    if (b & 1) {
      res = (res * a) % mod;
    }
    a = (a * a) % mod;
    b /= 2;
  }
  return res;
}
void solve() {
  ll n, m;

  cin >> n >> m;

  vector<ll> adj[n + 1];

  for (ll i = 0; i < m; i++) {
    ll x, y;
    cin >> x >> y;

    adj[x].pb(y);
    adj[y].pb(x);
  }

  bool mark[n + 1];
  memset(mark, false, sizeof(mark));

  ll cnt = 0;

  function<void(ll, ll)> dfs = [&](ll s, ll p) {
    mark[s] = true;

    for (auto a : adj[s]) {
      if (a == p)
        continue;

      dfs(a, s);
    }
  };

  for (ll i = 1; i <= n; i++) {
    if (!mark[i]) {
      cnt++;
      dfs(i, -1);
    }
  }

  cnt--;

  cout << cnt << endl;
}
int32_t main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);

  // Start from Here.

  ll t;
  t = 1;
  // cin>>t;
  while (t--)
    solve();

  // Good Bye!
  return 0;
}

#include <bits/stdc++.h>
#define ll long long int
#define mk make_pair
#define pb push_back
#define INF (ll)1e18
#define pii pair<ll, ll>
#define mod 1000000007 // 998244353
#define f(i, a, b) for (ll i = a; i < b; i++)
#define fb(i, a, b) for (ll i = a; i > b; i--)
#define ff first
#define ss second
#define srt(v)                                                                 \\
  if (!v.empty())                                                              \\
  sort(v.begin(), v.end())
#define PI 3.141592653589793238
#define pq priority_queue<ll>
#define pqr priority_queue<ll, vector<ll>, greater<ll>()>
using namespace std;
ll pow_mod(ll a, ll b) {
  ll res = 1;
  while (b != 0) {
    if (b & 1) {
      res = (res * a) % mod;
    }
    a = (a * a) % mod;
    b /= 2;
  }
  return res;
}
void solve() {
  ll n, m;

  cin >> n >> m;

  vector<ll> adj[n + 1];

  for (ll i = 0; i < m; i++) {
    ll x, y;
    cin >> x >> y;

    adj[x].pb(y);
    adj[y].pb(x);
  }

  bool mark[n + 1];
  memset(mark, false, sizeof(mark));

  ll cnt = 0;

  for (ll i = 1; i <= n; i++) {
    if (!mark[i]) {
      cnt++;
      queue<ll> q;

      q.push(i);

      mark[i] = true;

      while (!q.empty()) {
        ll x = q.front();
        mark[x] = true;
        q.pop();

        for (auto a : adj[x]) {
          if (!mark[a]) {
            mark[a] = true;
            q.push(a);
          }
        }
      }
    }
  }

  cnt--;

  cout << cnt << endl;
}
int32_t main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  cout.tie(NULL);

  // Start from Here.

  ll t;
  t = 1;
  // cin>>t;
  while (t--)
    solve();

  // Good Bye!
  return 0;
}


#include <iostream>
#include <map>
#include <string>
using namespace std;

int par[100000]; // 添え字として0~99999を許容する

int root(int i) {
  if (par[i] == i) {
    return i;
  }
  return root(par[i]);
}
int main() {
  int n, m;
  cin >> n >> m;
  for (int i = 0; i < n; ++i) {
    par[i] = i;
  }
  for (int i = 0; i < m; ++i) {
    int k1, k2;
    cin >> k1 >> k2;
    k1--;
    k2--;
    par[root(k1)] = root(k2);
  }
  int cnt = 0;
  for (int i = 0; i < n; ++i) {
    if (root(i) == i) {
      cnt++;
    }
  }
  cout << (cnt - 1);
}

#include <iostream>
#include <map>
#include <string>
using namespace std;

int par[100000]; // 添え字として0~99999を許容する

int root(int i) {
  if (par[i] == i) {
    return i;
  }
  return par[i] = root(par[i]);
}
int main() {
  int n, m;
  cin >> n >> m;
  for (int i = 0; i < n; ++i) {
    par[i] = i;
  }
  for (int i = 0; i < m; ++i) {
    int k1, k2;
    cin >> k1 >> k2;
    k1--;
    k2--;
    par[root(k1)] = root(k2);
  }
  int cnt = 0;
  for (int i = 0; i < n; ++i) {
    if (root(i) == i) {
      cnt++;
    }
  }
  cout << (cnt - 1);
}


#include <iostream>
#include <map>
#include <vector>
using namespace std;

struct UnionFind {
  vector<int> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2

  UnionFind(int N) : par(N) { // 最初は全てが根であるとして初期化
    for (int i = 0; i < N; i++)
      par[i] = i;
  }

  int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
    if (par[x] == x)
      return x;
    return par[x] = root(par[x]);
  }

  void unite(int x, int y) { // xとyの木を併合
    int rx = root(x);        // xの根をrx
    int ry = root(y);        // yの根をry
    if (rx == ry)
      return; // xとyの根が同じ(=同じ木にある)時はそのまま
    par[rx] =
        ry; // xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
  }

  bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
    int rx = root(x);
    int ry = root(y);
    return rx == ry;
  }
};

int main() {
  int N, M;
  cin >> N >> M;
  UnionFind tree(N);

  for (int i = 0; i < M; i++) {
    int A, B;
    cin >> A >> B;
    tree.unite(A, B);
  }

  int ans{0};
  map<int, int> cnt;
  for (int i = 0; i < N; i++) {
    cnt[tree.par.at(i)]++;
  }

  cout << cnt.size() - 1 << endl;
  return 0;
}

#include <iostream>
#include <map>
#include <vector>
using namespace std;

struct UnionFind {
  vector<int> par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2

  UnionFind(int N) : par(N) { // 最初は全てが根であるとして初期化
    for (int i = 0; i < N; i++)
      par[i] = i;
  }

  int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
    if (par[x] == x)
      return x;
    return par[x] = root(par[x]);
  }

  void unite(int x, int y) { // xとyの木を併合
    int rx = root(x);        // xの根をrx
    int ry = root(y);        // yの根をry
    if (rx == ry)
      return; // xとyの根が同じ(=同じ木にある)時はそのまま
    par[rx] =
        ry; // xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
  }

  bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
    int rx = root(x);
    int ry = root(y);
    return rx == ry;
  }
};

int main() {
  int N, M;
  cin >> N >> M;
  UnionFind tree(N + 1);

  for (int i = 0; i < M; i++) {
    int A, B;
    cin >> A >> B;
    tree.unite(A, B);
  }

  map<int, int> cnt;
  for (int i = 1; i <= N; i++) {
    cnt[tree.root(i)]++;
  }

  for (int i = 1; i <= N; i++) {
    cerr << tree.root(i) << endl;
  }

  cout << cnt.size() - 1 << endl;
  return 0;
}


#include <bits/stdc++.h>
using namespace std;
// using Graph = vector<vector<int>>;

struct UnionFind {
  vector<int> par;
  vector<int> rank;
  int num;
  UnionFind(int N) {
    num = N;
    rank.resize(N);
    par.resize(N);
    for (int i = 0; i < N; i++) {
      rank[i] = 0;
      par[i] = i;
    }
  }
  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]) {
      swap(x, y);
    }
    if (rank[x] == rank[y])
      rank[y]++;
    par[x] = y;
    num--;
  }
  bool same(int x, int y) { return find(x) == find(y); }
  int count() const { return num; }
};

int main() {
  int n, m;
  cin >> n >> m;
  UnionFind uf(n);
  int ans = n - 1;
  for (int i = 0; i < m; i++) {
    int a, b;
    cin >> a >> b;
    uf.unite(a, b);
  }
  cout << uf.count() - 1 << endl;
}

#include <bits/stdc++.h>
using namespace std;
// using Graph = vector<vector<int>>;

struct UnionFind {
  vector<int> par;
  vector<int> rank;
  int num;
  UnionFind(int N) {
    num = N;
    rank.resize(N);
    par.resize(N);
    for (int i = 0; i < N; i++) {
      rank[i] = 0;
      par[i] = i;
    }
  }
  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]) {
      swap(x, y);
    }
    if (rank[x] == rank[y])
      rank[y]++;
    par[x] = y;
    num--;
  }
  bool same(int x, int y) { return find(x) == find(y); }
  int count() const { return num; }
};

int main() {
  int n, m;
  cin >> n >> m;
  UnionFind uf(n);
  int ans = n - 1;
  for (int i = 0; i < m; i++) {
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    uf.unite(a, b);
  }
  cout << uf.count() - 1 << endl;
}


/*
        Author: Ritik Patel
*/

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& STL DEBUGGER
//&&&&&&&&&&&&&&&&&&&&&&&&&&&

// #define _GLIBCXX_DEBUG       // Iterator safety; out-of-bounds access for
// Containers, etc. #pragma GCC optimize "trapv" // abort() on (signed) integer
// overflow.

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& LIBRARIES &&&&&&&&&&&&&&&&&&&&&&&&&&&

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

/*#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp> // Including
tree_order_statistics_node_update template<typename T, typename V =
__gnu_pbds::null_type> using ordered_set = __gnu_pbds::tree<T, V, less<T>,
__gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update>;
*/
// find_by_order()->returns an iterator to the k-th largest element(0-based
// indexing) order_of_key()->Number of items that are strictly smaller than our
// item

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& DEFINES &&&&&&&&&&&&&&&&&&&&&&&&&&&

#define int long long int
// #define ll long long int
#define all(i) i.begin(), i.end()
#define sz(a) (int)a.size()

// #define ld long double
// const ld PI  = 3.141592;
const int dx4[4] = {0, 1, 0, -1};
const int dy4[4] = {-1, 0, 1, 0};
const int dx8[8] = {-1, -1, -1, 0, 1, 1, 1, 0};
const int dy8[8] = {-1, 0, 1, 1, 1, 0, -1, -1};

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& DEBUG &&&&&&&&&&&&&&&&&&&&&&&&&&&

#define XOX
vector<string> vec_splitter(string s) {
  for (char &c : s)
    c = c == ',' ? ' ' : c;
  stringstream ss;
  ss << s;
  vector<string> res;
  for (string z; ss >> z; res.push_back(z))
    ;
  return res;
}

void debug_out(vector<string> __attribute__((unused)) args,
               __attribute__((unused)) int idx) {
  cerr << endl;
}
template <typename Head, typename... Tail>
void debug_out(vector<string> args, int idx, Head H, Tail... T) {
  if (idx > 0)
    cerr << ", ";
  stringstream ss;
  ss << H;
  cerr << args[idx] << " = " << ss.str();
  debug_out(args, idx + 1, T...);
}

#ifdef XOX
#define debug(...) debug_out(vec_splitter(#__VA_ARGS__), 0, __VA_ARGS__)
#else
#define debug(...) 42
#endif

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& CODE &&&&&&&&&&&&&&&&&&&&&&&&&&&

int ans = 0;
const int MAXN = 1e4 + 5;
vector<bool> vis(MAXN, false);
vector<bool> considered(MAXN, false);
vector<int> g[MAXN];

void dfs(int u) {
  vis[u] = true;
  for (auto &v : g[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;
    g[x].push_back(y);
    g[y].push_back(x);
    considered[x] = true;
    considered[y] = true;
  }
  // ans is no of cycles
  int add = 0;
  for (int i = 1; i <= N; ++i) {
    if (!considered[i]) {
      ++add;
      continue;
    }
    if (!vis[i]) {
      dfs(i);
      ++ans;
    }
  }

  cout << ans + add - 1 << '\\n';
}

int32_t main() {
  // freopen("input.txt","r",stdin);
  // freopen("output.txt","w",stdout);
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);
  cout.tie(nullptr);
  int T = 1;
  // cin >> T;
  for (int i = 1; i <= T; ++i) {
    // cout << "Case #" << i << ": ";
    solve();
  }
  return 0;
}

/*
Sample inp
*/

/*
        Author: Ritik Patel
*/

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& STL DEBUGGER
//&&&&&&&&&&&&&&&&&&&&&&&&&&&

// #define _GLIBCXX_DEBUG       // Iterator safety; out-of-bounds access for
// Containers, etc. #pragma GCC optimize "trapv" // abort() on (signed) integer
// overflow.

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& LIBRARIES &&&&&&&&&&&&&&&&&&&&&&&&&&&

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

/*#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp> // Including
tree_order_statistics_node_update template<typename T, typename V =
__gnu_pbds::null_type> using ordered_set = __gnu_pbds::tree<T, V, less<T>,
__gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update>;
*/
// find_by_order()->returns an iterator to the k-th largest element(0-based
// indexing) order_of_key()->Number of items that are strictly smaller than our
// item

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& DEFINES &&&&&&&&&&&&&&&&&&&&&&&&&&&

#define int long long int
// #define ll long long int
#define all(i) i.begin(), i.end()
#define sz(a) (int)a.size()

// #define ld long double
// const ld PI  = 3.141592;
const int dx4[4] = {0, 1, 0, -1};
const int dy4[4] = {-1, 0, 1, 0};
const int dx8[8] = {-1, -1, -1, 0, 1, 1, 1, 0};
const int dy8[8] = {-1, 0, 1, 1, 1, 0, -1, -1};

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& DEBUG &&&&&&&&&&&&&&&&&&&&&&&&&&&

#define XOX
vector<string> vec_splitter(string s) {
  for (char &c : s)
    c = c == ',' ? ' ' : c;
  stringstream ss;
  ss << s;
  vector<string> res;
  for (string z; ss >> z; res.push_back(z))
    ;
  return res;
}

void debug_out(vector<string> __attribute__((unused)) args,
               __attribute__((unused)) int idx) {
  cerr << endl;
}
template <typename Head, typename... Tail>
void debug_out(vector<string> args, int idx, Head H, Tail... T) {
  if (idx > 0)
    cerr << ", ";
  stringstream ss;
  ss << H;
  cerr << args[idx] << " = " << ss.str();
  debug_out(args, idx + 1, T...);
}

#ifdef XOX
#define debug(...) debug_out(vec_splitter(#__VA_ARGS__), 0, __VA_ARGS__)
#else
#define debug(...) 42
#endif

//&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& CODE &&&&&&&&&&&&&&&&&&&&&&&&&&&

int ans = 0;
const int MAXN = 1e5 + 5;
vector<bool> vis(MAXN, false);
vector<bool> considered(MAXN, false);
vector<int> g[MAXN];

void dfs(int u) {
  vis[u] = true;
  for (auto &v : g[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;
    g[x].push_back(y);
    g[y].push_back(x);
    considered[x] = true;
    considered[y] = true;
  }
  // ans is no of cycles
  int add = 0;
  for (int i = 1; i <= N; ++i) {
    if (!considered[i]) {
      ++add;
      continue;
    }
    if (!vis[i]) {
      dfs(i);
      ++ans;
    }
  }

  cout << ans + add - 1 << '\\n';
}

int32_t main() {
  // freopen("input.txt","r",stdin);
  // freopen("output.txt","w",stdout);
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);
  cout.tie(nullptr);
  int T = 1;
  // cin >> T;
  for (int i = 1; i <= T; ++i) {
    // cout << "Case #" << i << ": ";
    solve();
  }
  return 0;
}

/*
Sample inp
*/


#include <bits/stdc++.h>

using namespace std;
using ll = long long;
using Graph = vector<vector<int>>;
using pint = pair<int, int>;
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
const int MOD = 1000000007;
const long long INF = 1LL << 60;
template <class T> inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}

struct UnionFind {
  vector<int> par;
  UnionFind(int N) : par(N) {
    for (int i = 0; i < N; i++)
      par[i] = i;
  }
  int root(int x) {
    if (par[x] == x)
      return x;
    return par[x] = root(par[x]);
  }
  void unite(int x, int y) {
    int rx = root(x);
    int ry = root(y);
    if (rx == ry)
      return;
    par[rx] = ry;
  }
  bool same(int x, int y) {
    int rx = root(x);
    int ry = root(y);
    return rx == ry;
  }
};

int main() {
  int N, M;
  cin >> N >> M;
  UnionFind uf(N);
  for (int i = 0; i < M; ++i) {
    int A, B;
    --A, --B;
    cin >> A >> B;
    uf.unite(A, B);
  }
  int res = 0;
  for (int i = 0; i < N; ++i) {
    if (uf.par[i] == i) {
      ++res;
    }
  }
  cout << res - 1 << endl;

  return 0;
}

#include <bits/stdc++.h>

using namespace std;
using ll = long long;
using Graph = vector<vector<int>>;
using pint = pair<int, int>;
int dx[4] = {1, 0, -1, 0};
int dy[4] = {0, 1, 0, -1};
const int MOD = 1000000007;
const long long INF = 1LL << 60;
template <class T> inline bool chmax(T &a, T b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> inline bool chmin(T &a, T b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}

struct UnionFind {
  vector<int> par;
  UnionFind(int N) : par(N) {
    for (int i = 0; i < N; i++)
      par[i] = i;
  }
  int root(int x) {
    if (par[x] == x)
      return x;
    return par[x] = root(par[x]);
  }
  void unite(int x, int y) {
    int rx = root(x);
    int ry = root(y);
    if (rx == ry)
      return;
    par[rx] = ry;
  }
  bool same(int x, int y) {
    int rx = root(x);
    int ry = root(y);
    return rx == ry;
  }
};

int main() {
  int N, M;
  cin >> N >> M;
  UnionFind uf(N);
  for (int i = 0; i < M; ++i) {
    int A, B;
    cin >> A >> B;
    --A, --B;
    uf.unite(A, B);
  }
  int res = 0;
  for (int i = 0; i < N; ++i) {
    if (uf.par[i] == i) {
      ++res;
    }
  }
  cout << res - 1 << endl;

  return 0;
}


#include <algorithm>
#include <array>
#include <assert.h>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cfloat>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
int INF = 1e9 + 7;
int mod = 998244353;
long double PI =
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998626034825342117;
struct Edge {
  long long to;
  long long cost;
};
void dijkstra(
    const vector<vector<Edge>> &G, long long s,
    vector<long long> &dis) { // Gは隣接グラフ,sは始点,距離の結果をdisに格納
  long long N = G.size();
  dis.resize(N, INF);
  priority_queue<pair<int, int>, vector<pair<int, int>>,
                 greater<pair<int, int>>>
      pq; // 「仮の最短距離, 頂点」が小さい順に並ぶ
  dis[s] = 0;
  pq.emplace(dis[s], s);
  while (!pq.empty()) {
    pair<int, int> p = pq.top();
    pq.pop();
    int v = p.second;
    if (dis[v] < p.first) {
      continue;
    }
    for (auto &e : G[v]) {
      if (dis[e.to] > dis[v] + e.cost) {
        dis[e.to] = dis[v] + e.cost;
        pq.emplace(dis[e.to], e.to);
      }
    }
  }
  return;
}
long double kodohou(long double N) { return N * 180.0 / PI; }
struct mint {
  int x;
  mint(int x = 0) : x((x % mod + mod) % mod) {}
  mint operator-() const { return mint(-x); }
  mint &operator+=(const mint a) {
    if ((x += a.x) >= mod)
      x -= mod;
    return *this;
  }
  mint &operator-=(const mint a) {
    if ((x += mod - a.x) >= mod)
      x -= mod;
    return *this;
  }
  mint &operator*=(const mint a) {
    (x *= a.x) %= mod;
    return *this;
  }
  mint operator+(const mint a) const { return mint(*this) += a; }
  mint operator-(const mint a) const { return mint(*this) -= a; }
  mint operator*(const mint a) const { return mint(*this) *= a; }
  mint pow(int t) const {
    if (!t)
      return 1;
    mint a = pow(t >> 1);
    a *= a;
    if (t & 1)
      a *= *this;
    return a;
  }
  mint inv() const { return pow(mod - 2); }
  mint &operator/=(const mint a) { return *this *= a.inv(); }
  mint operator/(const mint a) const { return mint(*this) /= a; }
};
istream &operator>>(istream &is, mint &a) { return is >> a.x; }
ostream &operator<<(ostream &os, const mint &a) { return os << a.x; }
struct UnionFind {
  vector<int> par, size;
  UnionFind(int x) {
    par.resize(x);
    size.resize(x, 1);
    for (int i = 0; i < x; i++) {
      par[i] = i;
    }
  }
  int find(int x) {
    if (par[x] == x)
      return x;
    return par[x] = find(par[x]);
  }
  bool same(int x, int y) { return find(x) == find(y); }
  int consize(int x) { return size[find(x)]; }
  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y)
      return;
    if (size[x] < size[y]) {
      par[x] = y;
      size[y] += size[x];
    } else {
      par[y] = x;
      size[x] += size[y];
    }
  }
};
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;
}
long long modinv(long long a, long long m) {
  long long b = m, u = 1, v = 0;
  while (b) {
    long long t = a / b;
    a -= t * b;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  u %= m;
  if (u < 0)
    u += m;
  return u;
}
bool IsPrime(int num) {
  if (num < 2)
    return false;
  else if (num == 2)
    return true;
  else if (num % 2 == 0)
    return false;
  double sqrtNum = sqrt(num);
  for (int i = 3; i <= sqrtNum; i += 2) {
    if (num % i == 0) {
      return false;
    }
  }
  return true;
}
signed gcd(long long x, long long y) {
  if (y == 0)
    return x;
  return gcd(y, x % y);
}
signed lcm(long long x, long long y) { return x / gcd(x, y) * y; }
long long RepeatSquaring(long long N, long long P, long long M) {
  if (P == 0)
    return 1;
  if (P % 2 == 0) {
    long long t = RepeatSquaring(N, P / 2, M);
    return t * t % M;
  }
  return N * RepeatSquaring(N, P - 1, M);
}
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
long long Fibonacci(long long A) {
  if (A == 1)
    return 1;
  vector<long long> B(A + 1);
  B[0] = 1;
  B[1] = 1;
  for (int i = 2; i <= A; i++) {
    B[i] = B[i - 1] + B[i - 2];
    B[i] %= mod;
  }
  return B[A];
}
int mod_pow(int x, int y) {
  int res = 1;
  while (y > 0) {
    if (y % 2) {
      res = res * x % mod;
    }
    x = x * x % mod;
    y /= 2;
  }
  return res;
}
int fac[100005], finv[100005], inv[100005];
void COMinit() {
  fac[0] = fac[1] = 1;
  finv[0] = finv[1] = 1;
  inv[1] = 1;
  for (int i = 2; i < 100005; i++) {
    fac[i] = fac[i - 1] * i % mod;
    inv[i] = mod - inv[mod % i] * (mod / i) % mod;
    finv[i] = finv[i - 1] * inv[i] % mod;
  }
}
int COM(int n, int k) {
  if (n < k)
    return 0;
  if (n < 0 || k < 0)
    return 0;
  return fac[n] * (finv[k] * finv[n - k] % mod) % mod;
}
vector<int> sieve(int max) {
  vector<bool> prime(max + 1, true);
  vector<int> A;
  for (int i = 2; i <= max; ++i)
    if (prime[i]) {
      A.push_back(i);
      for (int j = 2; i * j <= max; ++j)
        prime[i * j] = false;
    }
  return A;
}
class smart_sieve {
  intmax_t L, R, M;
  vector<int> small;
  vector<vector<intmax_t>> large;
  vector<intmax_t> aux;

public:
  smart_sieve(intmax_t L, intmax_t R) : L(L), R(R), M(sqrt(R) + 1) {
    small.resize(M);
    iota(small.begin(), small.end(), 0);
    large.resize(R - L);
    aux.assign(R - L, 1);

    for (intmax_t i = 2; i * i < R; ++i) {
      if (small[i] < i)
        continue;
      small[i] = i;
      for (intmax_t j = i * i; j < M; j += i)
        if (small[j] == j)
          small[j] = i;

      for (intmax_t j = (L + i - 1) / i * i; j < R; j += i) {
        intmax_t k = j;
        do {
          if (aux[j - L] * aux[j - L] > R)
            break;

          large[j - L].push_back(i);
          aux[j - L] *= i;
          k /= i;
        } while (k % i == 0);
      }
    }
  }
};
int main() {
  int N, M;
  cin >> N >> M;
  vector<int> A(M);
  vector<int> B(M);
  UnionFind C(N);
  for (int i = 0; i < M; i++) {
    cin >> A[i] >> B[i];
    C.unite(A[i], B[i]);
  }
  map<int, int> D;
  for (int i = 0; i < N; i++) {
    D[C.par[i]]++;
  }
  int ans = 0;
  for (int i = 0; i < N + 2; i++) {
    if (D[i] != 0)
      ans++;
  }
  cout << ans - 1 << endl;
}

#include <algorithm>
#include <array>
#include <assert.h>
#include <bitset>
#include <cassert>
#include <cctype>
#include <cfloat>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <limits>
#include <list>
#include <map>
#include <memory>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
int INF = 1e9 + 7;
int mod = 998244353;
long double PI =
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998626034825342117;
struct Edge {
  long long to;
  long long cost;
};
void dijkstra(
    const vector<vector<Edge>> &G, long long s,
    vector<long long> &dis) { // Gは隣接グラフ,sは始点,距離の結果をdisに格納
  long long N = G.size();
  dis.resize(N, INF);
  priority_queue<pair<int, int>, vector<pair<int, int>>,
                 greater<pair<int, int>>>
      pq; // 「仮の最短距離, 頂点」が小さい順に並ぶ
  dis[s] = 0;
  pq.emplace(dis[s], s);
  while (!pq.empty()) {
    pair<int, int> p = pq.top();
    pq.pop();
    int v = p.second;
    if (dis[v] < p.first) {
      continue;
    }
    for (auto &e : G[v]) {
      if (dis[e.to] > dis[v] + e.cost) {
        dis[e.to] = dis[v] + e.cost;
        pq.emplace(dis[e.to], e.to);
      }
    }
  }
  return;
}
long double kodohou(long double N) { return N * 180.0 / PI; }
struct mint {
  int x;
  mint(int x = 0) : x((x % mod + mod) % mod) {}
  mint operator-() const { return mint(-x); }
  mint &operator+=(const mint a) {
    if ((x += a.x) >= mod)
      x -= mod;
    return *this;
  }
  mint &operator-=(const mint a) {
    if ((x += mod - a.x) >= mod)
      x -= mod;
    return *this;
  }
  mint &operator*=(const mint a) {
    (x *= a.x) %= mod;
    return *this;
  }
  mint operator+(const mint a) const { return mint(*this) += a; }
  mint operator-(const mint a) const { return mint(*this) -= a; }
  mint operator*(const mint a) const { return mint(*this) *= a; }
  mint pow(int t) const {
    if (!t)
      return 1;
    mint a = pow(t >> 1);
    a *= a;
    if (t & 1)
      a *= *this;
    return a;
  }
  mint inv() const { return pow(mod - 2); }
  mint &operator/=(const mint a) { return *this *= a.inv(); }
  mint operator/(const mint a) const { return mint(*this) /= a; }
};
istream &operator>>(istream &is, mint &a) { return is >> a.x; }
ostream &operator<<(ostream &os, const mint &a) { return os << a.x; }
struct UnionFind {
  vector<int> par, size;
  UnionFind(int x) {
    par.resize(x);
    size.resize(x, 1);
    for (int i = 0; i < x; i++) {
      par[i] = i;
    }
  }
  int find(int x) {
    if (par[x] == x)
      return x;
    return par[x] = find(par[x]);
  }
  bool same(int x, int y) { return find(x) == find(y); }
  int consize(int x) { return size[find(x)]; }
  void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y)
      return;
    if (size[x] < size[y]) {
      par[x] = y;
      size[y] += size[x];
    } else {
      par[y] = x;
      size[x] += size[y];
    }
  }
};
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;
}
long long modinv(long long a, long long m) {
  long long b = m, u = 1, v = 0;
  while (b) {
    long long t = a / b;
    a -= t * b;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  u %= m;
  if (u < 0)
    u += m;
  return u;
}
bool IsPrime(int num) {
  if (num < 2)
    return false;
  else if (num == 2)
    return true;
  else if (num % 2 == 0)
    return false;
  double sqrtNum = sqrt(num);
  for (int i = 3; i <= sqrtNum; i += 2) {
    if (num % i == 0) {
      return false;
    }
  }
  return true;
}
signed gcd(long long x, long long y) {
  if (y == 0)
    return x;
  return gcd(y, x % y);
}
signed lcm(long long x, long long y) { return x / gcd(x, y) * y; }
long long RepeatSquaring(long long N, long long P, long long M) {
  if (P == 0)
    return 1;
  if (P % 2 == 0) {
    long long t = RepeatSquaring(N, P / 2, M);
    return t * t % M;
  }
  return N * RepeatSquaring(N, P - 1, M);
}
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
long long Fibonacci(long long A) {
  if (A == 1)
    return 1;
  vector<long long> B(A + 1);
  B[0] = 1;
  B[1] = 1;
  for (int i = 2; i <= A; i++) {
    B[i] = B[i - 1] + B[i - 2];
    B[i] %= mod;
  }
  return B[A];
}
int mod_pow(int x, int y) {
  int res = 1;
  while (y > 0) {
    if (y % 2) {
      res = res * x % mod;
    }
    x = x * x % mod;
    y /= 2;
  }
  return res;
}
int fac[100005], finv[100005], inv[100005];
void COMinit() {
  fac[0] = fac[1] = 1;
  finv[0] = finv[1] = 1;
  inv[1] = 1;
  for (int i = 2; i < 100005; i++) {
    fac[i] = fac[i - 1] * i % mod;
    inv[i] = mod - inv[mod % i] * (mod / i) % mod;
    finv[i] = finv[i - 1] * inv[i] % mod;
  }
}
int COM(int n, int k) {
  if (n < k)
    return 0;
  if (n < 0 || k < 0)
    return 0;
  return fac[n] * (finv[k] * finv[n - k] % mod) % mod;
}
vector<int> sieve(int max) {
  vector<bool> prime(max + 1, true);
  vector<int> A;
  for (int i = 2; i <= max; ++i)
    if (prime[i]) {
      A.push_back(i);
      for (int j = 2; i * j <= max; ++j)
        prime[i * j] = false;
    }
  return A;
}
class smart_sieve {
  intmax_t L, R, M;
  vector<int> small;
  vector<vector<intmax_t>> large;
  vector<intmax_t> aux;

public:
  smart_sieve(intmax_t L, intmax_t R) : L(L), R(R), M(sqrt(R) + 1) {
    small.resize(M);
    iota(small.begin(), small.end(), 0);
    large.resize(R - L);
    aux.assign(R - L, 1);

    for (intmax_t i = 2; i * i < R; ++i) {
      if (small[i] < i)
        continue;
      small[i] = i;
      for (intmax_t j = i * i; j < M; j += i)
        if (small[j] == j)
          small[j] = i;

      for (intmax_t j = (L + i - 1) / i * i; j < R; j += i) {
        intmax_t k = j;
        do {
          if (aux[j - L] * aux[j - L] > R)
            break;

          large[j - L].push_back(i);
          aux[j - L] *= i;
          k /= i;
        } while (k % i == 0);
      }
    }
  }
};
int main() {
  int N, M;
  cin >> N >> M;
  vector<int> A(M);
  vector<int> B(M);
  UnionFind C(N);
  for (int i = 0; i < M; i++) {
    cin >> A[i] >> B[i];
    C.unite(A[i] - 1, B[i] - 1);
  }
  map<int, int> D;
  for (int i = 0; i < N; i++) {
    D[C.find(i)]++;
  }
  int ans = 0;
  for (int i = 0; i < N + 2; i++) {
    if (D[i] != 0)
      ans++;
  }
  cout << ans - 1 << endl;
}


#include <bits/stdc++.h>

using namespace std;
int a, m, n, b, kt[100009] = {0}, dem = 0, a1[10009][10009] = {0};
void bfs(int a) {
  kt[a] = dem;
  for (int i = 1; i <= n; i++) {
    if (a1[a][i] == 1 && kt[i] == 0)
      bfs(i);
  }
}
int main() {
  // freopen("tplt.inp","r",stdin);
  // freopen("tplt.out","w",stdout);
  cin >> n >> m;
  for (int i = 1; i <= m; i++) {
    cin >> a >> b;
    a1[a][b] = 1;
    a1[b][a] = 1;
  }
  for (int i = 1; i <= n; i++) {
    if (kt[i] == 0) {
      dem++;
      bfs(i);
    }
  }
  cout << dem - 1;
  return 0;
}

#include <bits/stdc++.h>

using namespace std;
int a, m, n, b, kt[100009], dem = 0;
vector<int> a1[100009];
void bfs(int a) {
  kt[a] = dem;
  for (int i : a1[a]) {
    if (kt[i] == 0)
      bfs(i);
  }
}
int main() {
  // freopen("tplt.inp","r",stdin);
  // freopen("tplt.out","w",stdout);
  cin >> n >> m;
  for (int i = 1; i <= 100009; i++)
    kt[i] = 0;
  for (int i = 1; i <= m; i++) {
    cin >> a >> b;
    a1[a].push_back(b);
    a1[b].push_back(a);
  }
  for (int i = 1; i <= n; i++) {
    if (kt[i] == 0) {
      dem++;
      bfs(i);
    }
  }
  cout << dem - 1;
  return 0;
}


#include <bits/stdc++.h>

#define pb push_back
#define PQ priority_queue
#define make_unique(x)                                                         \\
  sort((x).begin(), (x).end());                                                \\
  (x).erase(unique((x).begin(), (x).end()), (x).end())

typedef long long ll;
typedef long double ld;

//__builtin_popcount(x)

using namespace std;

/*const int mod = 1e9 + 7;

ll add(ll a, ll b) {
    ll res = a + b;
    if (res >= mod) res -= mod;
    return res;
}

ll sub(ll a, ll b) {
    ll res = a - b + mod;
    if (res >= mod) res -= mod;
    return res;
}

ll mul(ll a, ll b) {
    return (((a % mod) * (b % mod)) % mod);
}

int powm(int a, long long n) {
        if (n == 0) {
                return 1;
        }
        else if (n & 1) {
                return mul(a, powm(a, n - 1));
        }
        else {
                return powm(mul(a, a), n / 2);
        }
}*/

/* Z-FUNCTION
ll l = 0, r = 0, n = (int)s.size();
vector<int> z(n, 0);
for (int i = 1; i < n; i++) {
    if (r >= i) {
        z[i] = min(1ll*z[i-l], r-i+1);
    }
    while (z[i] + i < n && s[z[i]] == s[z[i] + i]) z[i]++;
    if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
*/

/*vector<ll> find_dividers(ll x) {
    vector<ll> dividers;

    for (ll i = 1; i <= sqrt(x); i++) {
        if (x % i == 0) {
           dividers.push_back(i);
            if (1ll*i * i != x) {
                dividers.push_back(1ll*x / 1ll*i);
            }
        }
    }

    return dividers;
}*/

/*int nok(int a, int b) {
    return a / __gcd(a, b) * b;
}*/

// ceil(a/b) = (a+b-1)/b
int n, m;
vector<vector<int>> gr;
vector<bool> used;

void dfs(int u) {
  used[u] = true;
  for (int i = 0; i < gr[u].size(); i++) {
    if (!used[gr[u][i]]) {
      dfs(gr[u][i]);
    }
  }
}

void solve() {
  cin >> n >> m;
  gr = vector<vector<int>>(n + 1, vector<int>(m));
  used.resize(n + 1);
  for (int i = 0; i < m; i++) {
    int x, y;
    cin >> x >> y;
    gr[x].pb(y);
    gr[y].pb(x);
  }

  int comp = 0;
  for (int i = 1; i <= n; i++) {
    if (!used[i]) {
      comp++;
      dfs(i);
    }
  }
  cout << comp - 1 << endl;
}

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  // cout.setf(ios::fixed);
  // cout.precision(10);

  int t = 1;
  // cin >> t;
  while (t--) {
    solve();
  }

  return 0;
}

#include <bits/stdc++.h>

#define pb push_back
#define PQ priority_queue
#define make_unique(x)                                                         \\
  sort((x).begin(), (x).end());                                                \\
  (x).erase(unique((x).begin(), (x).end()), (x).end())

typedef long long ll;
typedef long double ld;

//__builtin_popcount(x)

using namespace std;

/*const int mod = 1e9 + 7;

ll add(ll a, ll b) {
    ll res = a + b;
    if (res >= mod) res -= mod;
    return res;
}

ll sub(ll a, ll b) {
    ll res = a - b + mod;
    if (res >= mod) res -= mod;
    return res;
}

ll mul(ll a, ll b) {
    return (((a % mod) * (b % mod)) % mod);
}

int powm(int a, long long n) {
        if (n == 0) {
                return 1;
        }
        else if (n & 1) {
                return mul(a, powm(a, n - 1));
        }
        else {
                return powm(mul(a, a), n / 2);
        }
}*/

/* Z-FUNCTION
ll l = 0, r = 0, n = (int)s.size();
vector<int> z(n, 0);
for (int i = 1; i < n; i++) {
    if (r >= i) {
        z[i] = min(1ll*z[i-l], r-i+1);
    }
    while (z[i] + i < n && s[z[i]] == s[z[i] + i]) z[i]++;
    if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
*/

/*vector<ll> find_dividers(ll x) {
    vector<ll> dividers;

    for (ll i = 1; i <= sqrt(x); i++) {
        if (x % i == 0) {
           dividers.push_back(i);
            if (1ll*i * i != x) {
                dividers.push_back(1ll*x / 1ll*i);
            }
        }
    }

    return dividers;
}*/

/*int nok(int a, int b) {
    return a / __gcd(a, b) * b;
}*/

// ceil(a/b) = (a+b-1)/b
int n, m;
vector<vector<int>> gr;
vector<bool> used;

void dfs(int u) {
  used[u] = true;
  for (int i = 0; i < (int)gr[u].size(); i++) {
    if (!used[gr[u][i]]) {
      dfs(gr[u][i]);
    }
  }
}

void solve() {
  cin >> n >> m;
  gr = vector<vector<int>>(n + 1);
  used.resize(n + 1);
  for (int i = 0; i < m; i++) {
    int x, y;
    cin >> x >> y;
    gr[x].pb(y);
    gr[y].pb(x);
  }

  int comp = 0;
  for (int i = 1; i <= n; i++) {
    if (!used[i]) {
      comp++;
      dfs(i);
    }
  }
  cout << comp - 1 << endl;
}

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(NULL);
  // cout.setf(ios::fixed);
  // cout.precision(10);

  int t = 1;
  // cin >> t;
  while (t--) {
    solve();
  }

  return 0;
}


#include <bits/stdc++.h>
using namespace std;
class DisjointSet {
public:
  vector<int> parent;
  vector<int> size;
  int totalSize;
  void init(int n) {
    parent.clear();
    parent.resize(n);
    size.resize(n);
    for (int i = 0; i < n; i++)
      parent[i] = i;
    for (int i = 0; i < n; i++)
      size[i] = 1;
    totalSize = n;
  }
  int getSuperParent(int x) {
    if (x == parent[x])
      return x;
    return parent[x] = getSuperParent(parent[x]);
  }
  void unite(int x, int y) {
    int a = getSuperParent(x), b = getSuperParent(y);
    if (a != b) {
      parent[a] = b;
      size[b] += size[a];
      size[a] = 0;
      totalSize--;
    }
    return;
  }
};
int main() {
  int n, m;
  cin >> n >> m;
  DisjointSet j;
  j.init(n);
  vector<pair<int, int>> v;
  while (m--) {
    int a, b;
    cin >> a >> b;
    v.push_back(make_pair(a, b));
  }
  int ans = 0;
  for (int i = 0; i < v.size(); i++) {
    int u = v[i].first, b = v[i].second;
    if (j.getSuperParent(u) != j.getSuperParent(b)) {
      j.unite(u, b);
      ans++;
    }
  }
  cout << ans << endl;
  return 0;
}

#include <bits/stdc++.h>
/*#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>*/

using namespace std;
// using namespace __gnu_pbds;

// typedef tree<int, null_type, less_equal<int>, rb_tree_tag,
// tree_order_statistics_node_update> ordered_set;

const int MAX_N = 1e5 + 5;
const int MAX_L = 20; // ~ Log N
const long long MOD = 1e9 + 7;
const long long INF = 1e18 + 42;

typedef long long ll;
typedef vector<int> vi;
typedef pair<int, int> ii;
typedef pair<int, pair<int, int>> ppi;
typedef vector<ii> vii;
typedef vector<vi> vvi;
#define pb push_back
#define mp make_pair
#define f first
#define s second
#define endl '\\n'
#define FOR(i, n) for (int i = 0; i < n; i++)
#define FORS(s) for (int i = 0; i < s.length(); i++)
#define input                                                                  \\
  int t;                                                                       \\
  cin >> t;                                                                    \\
  while (t--)
#define SORT(arr) sort(arr, arr + n);
#define LSOne(S) (S & (-S))
#define isBitSet(S, i) ((S >> i) & 1)
#define mod 998244353
#define MAXN 1000010
ll mE(ll x, ll y) {
  ll result = 1;
  while (y) {
    if (y & 1)
      result = ((result % MOD) * (x % MOD)) % MOD;
    x = ((x % MOD) * (x % MOD)) % MOD;
    y >>= 1;
  }
  return result % MOD;
}
int noOfDigits(ll k) {
  int cnt = 0;
  while (k) {
    cnt++;
    k /= 10;
  }
  return cnt;
}
class DisJointSet {
public:
  vector<int> par;
  vector<int> size;
  int total_size;
  void init(int n) {
    par.resize(n);
    size.resize(n);
    for (int i = 0; i < n; i++)
      par[i] = i;
    for (int i = 0; i < n; i++)
      size[i] = 1;
    total_size = n;
  }
  int get_superParent(int x) {
    if (x == par[x])
      return x;
    else
      return par[x] = get_superParent(par[x]); // Path Compression
  }
  void unite(int x, int y) {
    int superParentX = get_superParent(x);
    int superParentY = get_superParent(y);
    if (superParentX != superParentY) {
      par[superParentX] = superParentY;
      size[superParentY] += size[superParentX];
      size[superParentX] = 0;
      total_size--; // When two different components get connected, then their
                    // total size reduces by 1 as they become common and their
                    // unique identity is vanished..
    }
  }
};
vector<int> graph[100005];
vector<bool> visited(100005, false);
void dfs(int src) {
  visited[src] = true;
  for (int it : graph[src]) {
    if (!visited[it])
      dfs(it);
  }
  return;
}
void solve() {
  int n, m;
  cin >> n >> m;
  while (m--) {
    int x, y;
    cin >> x >> y;
    x--, y--;
    graph[x].pb(y);
    graph[y].pb(x);
  }
  ll ans = 0;
  for (ll i = 0; i < n; i++) {
    if (!visited[i]) {
      dfs(i);
      ans++;
    }
  }
  ans--;
  cout << ans << endl;
  return;
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  // input{
  // cout << "Case #" << t  << ": ";
  solve();
  //}
  return 0;
}


#include <cstdio>
#include <vector>

using namespace std;

int a[10005];
vector<int> g[10005];
bool vis[10005];

void dfs(int x) {
  vis[x] = true;
  for (int i : g[x]) {
    if (!vis[i]) {
      dfs(i);
    }
  }
}

int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  for (int i = 0; i < m; i++) {
    int a, b;
    scanf("%d%d", &a, &b);
    g[a].push_back(b);
    g[b].push_back(a);
  }
  int cnt = 0;
  for (int i = 1; i <= n; i++) {
    if (!vis[i]) {
      cnt++;
      dfs(i);
    }
  }
  printf("%d\\n", cnt - 1);
  return 0;
}

#include <cstdio>
#include <vector>

using namespace std;

const int N = 1e5 + 5;
int a[N];
vector<int> g[N];
bool vis[N];

void dfs(int x) {
  vis[x] = true;
  for (int i : g[x]) {
    if (!vis[i]) {
      dfs(i);
    }
  }
}

int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  for (int i = 0; i < m; i++) {
    int a, b;
    scanf("%d%d", &a, &b);
    g[a].push_back(b);
    g[b].push_back(a);
  }
  int cnt = 0;
  for (int i = 1; i <= n; i++) {
    if (!vis[i]) {
      cnt++;
      dfs(i);
    }
  }
  printf("%d\\n", cnt - 1);
  return 0;
}


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, b) for (ll i = (a); i < (b); i++)
#define REP(i, n) rep(i, 0, n)

struct UnionFind {
  vector<int> data;

  UnionFind(int sz) { data.assign(sz, -1); }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if (x == y)
      return (false);
    if (data[x] > data[y])
      swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if (data[k] < 0)
      return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) { return (-data[find(k)]); }

  int cnt() {
    int cnt = 0;
    for (auto it : data)
      if (it < 0)
        cnt++;
    return cnt;
  }
};

void solve() {
  int n, m;
  cin >> n >> m;
  UnionFind uf(n);
  REP(i, m) {
    int a, b;
    cin >> a >> b;
    uf.unite(a, b);
  }
  cout << uf.cnt() - 1 << endl;
}

int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  solve();
  return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, a, b) for (ll i = (a); i < (b); i++)
#define REP(i, n) rep(i, 0, n)

struct UnionFind {
  vector<int> data;

  UnionFind(int sz) { data.assign(sz, -1); }

  bool unite(int x, int y) {
    x = find(x), y = find(y);
    if (x == y)
      return (false);
    if (data[x] > data[y])
      swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }

  int find(int k) {
    if (data[k] < 0)
      return (k);
    return (data[k] = find(data[k]));
  }

  int size(int k) { return (-data[find(k)]); }

  int cnt() {
    int cnt = 0;
    for (auto it : data)
      if (it < 0)
        cnt++;
    return cnt;
  }
};

void solve() {
  int n, m;
  cin >> n >> m;
  UnionFind uf(n);
  REP(i, m) {
    int a, b;
    cin >> a >> b;
    a--;
    b--;
    uf.unite(a, b);
  }
  cout << uf.cnt() - 1 << endl;
}

int main() {
  cin.tie(0);
  ios::sync_with_stdio(false);
  solve();
  return 0;
}


#include <bits/stdc++.h>

using namespace std;
#define rep(i, n) for (int i = 0; i < (n); i++)
#define rep1(i, n) for (int i = 1; i <= (n); i++)
#define all(v) v.begin(), v.end()
#define ve vector
#define vi vector<int>
#define vi2 vector<vi>
#define vl vector<long long>
#define vl2 vector<vl>

template <class T> T maxAny(T a, T b) {
  if (a > b)
    return a;
  else
    return b;
}

template <class T> T minAny(T a, T b) {
  if (a > b)
    return b;
  else
    return a;
}

vector<int> mapping(100000);

int search(int a) {
  queue<int> q;
  int nexta = a;
  while (mapping[nexta] != nexta) {
    q.push(nexta);
    nexta = mapping[a];
  }
  while (!q.empty()) {
    mapping[q.front()] = nexta;
    q.pop();
  }
  return nexta;
}

int main() {
  int n, m;
  cin >> n >> m;
  int a[m], b[m];
  mapping.resize(n + 1);
  rep(i, m) cin >> a[i] >> b[i];
  mapping[0] = -1;
  rep1(i, n) { mapping[i] = i; }
  rep(i, m) {
    int a_moto = search(a[i]), b_moto = search(b[i]);
    if (mapping[a_moto] != mapping[b_moto])
      mapping[b_moto] = a_moto;
  }
  int ans = -1;
  rep1(i, n) {
    if (mapping[i] == i)
      ans++;
  }
  cout << ans << endl;
}

#include <bits/stdc++.h>

using namespace std;
#define rep(i, n) for (int i = 0; i < (n); i++)
#define rep1(i, n) for (int i = 1; i <= (n); i++)
#define all(v) v.begin(), v.end()
#define ve vector
#define vi vector<int>
#define vi2 vector<vi>
#define vl vector<long long>
#define vl2 vector<vl>

template <class T> T maxAny(T a, T b) {
  if (a > b)
    return a;
  else
    return b;
}

template <class T> T minAny(T a, T b) {
  if (a > b)
    return b;
  else
    return a;
}

vector<int> mapping(100000);

int search(int a) {
  queue<int> q;
  int nexta = a;
  while (mapping[nexta] != nexta) {
    q.push(nexta);
    nexta = mapping[nexta];
  }
  while (!q.empty()) {
    mapping[q.front()] = nexta;
    q.pop();
  }
  return nexta;
}

int main() {
  int n, m;
  cin >> n >> m;
  int a[m], b[m];
  mapping.resize(n + 1);
  rep(i, m) cin >> a[i] >> b[i];
  mapping[0] = -1;
  rep1(i, n) { mapping[i] = i; }
  rep(i, m) {
    int a_moto = search(a[i]), b_moto = search(b[i]);
    if (mapping[a_moto] != mapping[b_moto])
      mapping[b_moto] = a_moto;
  }
  int ans = -1;
  rep1(i, n) {
    if (mapping[i] == i)
      ans++;
  }
  cout << ans << endl;
}
/*
int main(){
    int n, k;
    cin >> n >> k;
    int a[n];
    rep(i, n) cin >> a[i];

}*/