import std.algorithm;
import std.container;
import std.conv;
import std.math;
import std.range;
import std.stdio;
import std.string;
import std.traits;


class UnionFind(T) if (isIntegral!T) {
private:
  T[] rank;
  T[] par;
  T size;

public:
  this(T size) {
    rank = repeat(0.to!T).take(size).array;
    par = iota(size).array;
    this.size = size;
  }

  T length() {
    return size;
  }

  T getRoot(T n) {
    return par[n] = (par[n] == n ? n : getRoot(par[n]));
  }

  bool unite(T n1, T n2) {
    n1 = getRoot(n1);
    n2 = getRoot(n2);
    if (n1 == n2) return false;
    
    bool t = rank[n1] < rank[n2];
    par[t ? n1 : n2] = t ? n2 : n1;
    if (rank[n1] == rank[n2]) rank[n1]++;
    return true;
  }

  bool haveSameRoot(T n1, T n2) {
    return getRoot(n1) == getRoot(n2);
  }

  T countRoots() {
    T roots = 0;
    par.each!((i, v){ if (getRoot(v) == i) roots++; });
    return roots;
  }
}

void main() {
  auto nm = readln.chomp.split.to!(int[]);
  auto n = nm[0];
  auto m = nm[1];

  auto uf = new UnionFind!int(n);
  iota(m).each!((_){
    auto xyz = readln.chomp.split.to!(int[]);
    uf.unite(xyz[0] - 1, xyz[1] - 1);
  });

  uf.countRoots.writeln;
}

