// Slower C++ solution:
//#pragma GCC target("avx2")

//#pragma GCC optimize("O3")

   

//#include <x86intrin.h>

#include <bits/stdc++.h>   

#include <ext/pb_ds/assoc_container.hpp>

#include <ext/pb_ds/tree_policy.hpp>

 

using namespace __gnu_pbds;

using namespace std;



template<typename T> using ordered_set = tree <T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

     

#define F first

#define S second           

#define lb lower_bound               

#define ub upper_bound

#define pb push_back

#define pf push_front    

#define ppb pop_back

#define mp make_pair                                     

#define bpp __builtin_popcountll                                                                                        

#define sqr(x) ((x) * (x)) 

#define al 0x3F3F3F3F

#define sz(x) (int)x.size()

#define all(x) x.begin(), x.end()

#define in insert

#define ppf pop_front

#define endl '\n'

//#define int long long

 

typedef unsigned long long ull;

typedef long long ll;

typedef long double ld;

typedef pair <int, int> pii;

 

const int mod = (int)1e9 + 7;

const int N = (int)3e5 + 123;

const ll inf = (ll)1e18 + 1;



const double pi = acos (-1.0);

const double eps = 1e-7;                  

const int dx[] = {0, 0, 1, 0, -1};

const int dy[] = {0, 1, 0, -1, 0};



int n, m, x[N], y[N], z[N], ans;

vector <int> g[N];

bool used[N];



void dfs (int v) {

   used[v] = 1;

   for (auto to : g[v])

      if (!used[to])

         dfs (to);

}



inline void boost () {                    

   ios_base :: sync_with_stdio (NULL);

   cin.tie (NULL), cout.tie (NULL);          

}                                                     



inline void Solve () {

   cin >> n >> m;

   for (int i = 1; i <= m; i ++) {

      cin >> x[i] >> y[i] >> z[i];

      g[x[i]].pb (y[i]);

      g[y[i]].pb (x[i]);

   }

   for (int i = 1; i <= n; i ++)

      if (!used[i])

         dfs (i), ans ++;

   cout << ans;

}



main () {                                       

// freopen (".in", "r", stdin);    

// freopen (".out", "w", stdout);              

   boost ();

   int tt = 1;

   //cin >> tt;   

   while (tt --) {

      Solve ();

   }                                               

   return 0;      

}

                          

// Faster C++ solution for the same problem (measured speedup: 9.173905779916561x):
#include <bits/stdc++.h>



using namespace std;

using ii = pair<int, int>;



class UFDS

{

private:

    std::vector<int> size, ps;

    int count;



public:

    UFDS(int N) : size(N + 1, 1), ps(N + 1), count(N)

    {

        std::iota(ps.begin(), ps.end(), 0);

    }



    int find_set(int x) 

    {

        return x == ps[x] ? x : (ps[x] = find_set(ps[x]));

    }



    bool same_set(int x, int y)

    {

        return find_set(x) == find_set(y);        

    }



    void union_set(int x, int y)

    {

        if (same_set(x, y))

            return;



        int p = find_set(x);

        int q = find_set(y);



        if (size[p] < size[q])

            std::swap(p, q);



        ps[q] = p;

        size[p] += size[q];

        --count;

    }



    int get_count() const { return count; }

};



int solve(int N, const vector<ii>& es)

{

    UFDS ufds(N);



    for (auto p : es)

    {

        auto x = p.first, y = p.second;



        ufds.union_set(x, y);

    }



    return ufds.get_count();

}



int main()

{

    ios::sync_with_stdio(false);



    int N, M;

    cin >> N >> M;



    vector<ii> es(M);



    for (int i = 0; i < M; ++i)

    {

        int x, y, z;

        cin >> x >> y >> z;



        es.push_back(ii(x, y));

    }



    cout << solve(N, es) << endl;



    return 0;

}