// Slower C++ solution:
//code by lynmisakura.wish to be accepted

/****************************/

#include<iostream>

#include<math.h>

#include<vector>

#include<string>

#include<stack>

#include<queue>

#include<map>

#include<algorithm>

#include<bitset>

#include <climits>

#include<set>

#include<bitset>

using namespace std;

/***************************/

typedef long long ll;

typedef pair<int,int> pi;

typedef vector<pair<int,int>> vpi;

 

const long long Mod = 998244353;

const long long mod = 1000000007;

const long long INF = 10000000000000;

 

#define itn int

#define endl "\n"

#define pb push_back

#define mp make_pair

#define mins(x,y) x = min(x,y)

#define maxs(x,y) x = max(x,y)

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

#define Rep(n) for(int i = 0;i < n;i++)

#define rep(i,n) for(int i = 0;i < n;i++)

#define UNIQUE(v) v.erase( unique(v.begin(), v.end()), v.end() )

 

ll gcd(ll a, ll b) { return b ? gcd(b, a%b) : a; }

ll qp(ll a, ll b) { int ans = 1; do { if (b & 1)ans = 1ll * ans*a; a = 1ll * a*a; } while (b >>= 1); return ans; }

ll qp(ll a, ll b, int mo) { int ans = 1; do { if (b & 1)ans = 1ll * ans*a%mo; a = 1ll * a*a%mo; } while (b >>= 1); return ans; }

 

#define _GLIBCXX_DEBUG

#define debug(x) cerr << #x << " = " << (x) << " (L" << __LINE__ << ")" << " " << __FILE__ << endl;

 

vector<vector<int>> G(100010,vector<int>(0));

int visit[100010];

void dfs(int n){

	visit[n] = 1;

	for(int i = 0;i < G[n].size();i++){

		if(visit[G[n][i]] == 0) dfs(G[n][i]);

	}

}

int main(void){

	int n,m;cin >> n >> m;

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

		int x,y,z;cin >> x >> y >> z;

		G[--x].pb(--y);

		G[y].pb(x);

	}

	int ans = 0;

	for(int i = 0;i < n;i++){

		if(visit[i] == 0){

			ans++;

			dfs(i);

		}

	}

	cout << ans << endl;

}

// Faster C++ solution for the same problem (measured speedup: 3.359404647708877x):
//code by lynmisakura.wish to be accepted!

#include<bits/stdc++.h>

using namespace std;



typedef long long ll;



class UnionFind {

    static const int MAX_N = 200020;

public:

    //UnionFind

    int par[MAX_N];

    int ufrank[MAX_N];

    int ufsize[MAX_N];

    UnionFind(int ufsize_of_tree) {

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

        {

            par[i] = i;

            ufrank[i] = 0;

            ufsize[i] = 1;

        }

    }

    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 (ufrank[x] < ufrank[y]) {

            par[x] = y;

            int sx = ufsize[x], sy = ufsize[y];

            ufsize[x] = ufsize[y] = sx + sy;

        }

        else {

            par[y] = x;

            if (ufrank[x] == ufrank[y]) ufrank[x]++;

            int sx = ufsize[x], sy = ufsize[y];

            ufsize[x] = ufsize[y] = sx + sy;

        }

    }

    bool same(int x, int y) {

        return find(x) == find(y);

    }

}; 



int main(void){

	int N,M;scanf("%d %d",&N,&M);

	UnionFind UF(N);

	int ans = N;

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

	{

		int x,y,z;scanf("%d %d %d",&x,&y,&z);

		if(!UF.same(x,y)){

			UF.unite(x,y);ans--;

		}

	}



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

}