import sys



input = sys.stdin.readline





class UnionFind:

    def __init__(self, n):

        self.v = [-1 for _ in range(n)]  # 根(負): 連結頂点数 * (-1) / 子(正): 根の頂点番号(0-indexed)



    def find(self, x):  # xを含む木における根の頂点番号を返す

        if self.v[x] < 0:  # (負)は根

            return x

        else:  # 根の頂点番号

            self.v[x] = self.find(self.v[x])  # uniteでは, 旧根に属する頂点の根が旧根のままなので更新

            return self.v[x]



    def unite(self, x, y):  # 違う根に属していたらrankが低くなるように連結

        x = self.find(x)

        y = self.find(y)

        if x == y:

            return

        if -self.v[x] < -self.v[y]:  # size比較, 　(-1) * (連結頂点数 * (-1)), (正)同士の大小比較

            x, y = y, x  # 連結頂点数が少ない方をyにすると, findでの更新回数が減る？

        self.v[x] += self.v[y]  # 連結頂点数の和を取る, 連結頂点数 * (-1)

        self.v[y] = x  # 連結頂点数が少ないy(引数yの根の頂点番号)の根をx(引数xの根の頂点番号)にする



    def root(self, x):

        return self.v[x] < 0  # (負)は根



    def same(self, x, y):

        return self.find(x) == self.find(y)  # 同じ根に属するか



    def size(self, x):

        return -self.v[self.find(x)]  # 連結頂点数を返す





n, m = list(map(int, input().split()))

uf = UnionFind(n)



for _ in range(m):

    x, y, z = list(map(int, input().split()))

    x -= 1

    y -= 1

    uf.unite(x, y)



print((sum(uf.root(j) for j in range(n))))

def main():

    from collections import deque

    import sys

    input = sys.stdin.readline



    N, M = list(map(int, input().split()))



    g = tuple(set() for _ in range(N))

    for _ in range(M):

        x, y, _ = (int(x) - 1 for x in input().split())

        g[x].add(y)

        g[y].add(x)



    ans = 0

    checked = [False] * N

    for s in range(N):

        if checked[s]: continue

        checked[s] = True

        ans += 1



        dq = deque([s])

        while dq:

            v = dq.popleft()

            for u in g[v]:

                if checked[u]: continue

                checked[u] = True

                dq.append(u)



    print(ans)





if __name__ == '__main__':

    main()

import sys

input = sys.stdin.readline


class UnionFind:
    def __init__(self, n):
        self.v = [-1 for _ in range(n)]  # 根(負): 連結頂点数 * (-1) / 子(正): 根の頂点番号(0-indexed)

    def find(self, x):  # xを含む木における根の頂点番号を返す
        if self.v[x] < 0:  # (負)は根
            return x
        else:  # 根の頂点番号
            self.v[x] = self.find(self.v[x])  # uniteでは, 旧根に属する頂点の根が旧根のままなので更新
            return self.v[x]

    def unite(self, x, y):  # 違う根に属していたらrankが低くなるように連結
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if -self.v[x] < -self.v[y]:  # size比較, 　(-1) * (連結頂点数 * (-1)), (正)同士の大小比較
            x, y = y, x  # 連結頂点数が少ない方をyにすると, findでの更新回数が減る？
        self.v[x] += self.v[y]  # 連結頂点数の和を取る, 連結頂点数 * (-1)
        self.v[y] = x  # 連結頂点数が少ないy(引数yの根の頂点番号)の根をx(引数xの根の頂点番号)にする

    def root(self, x):
        return self.v[x] < 0  # (負)は根

    def same(self, x, y):
        return self.find(x) == self.find(y)  # 同じ根に属するか

    def size(self, x):
        return -self.v[self.find(x)]  # 連結頂点数を返す


n, m = list(map(int, input().split()))
uf = UnionFind(n)
for _ in range(m):
    x, y, z = list(map(int, input().split()))
    x -= 1
    y -= 1
    uf.unite(x, y)
print((sum(uf.root(j) for j in range(n))))

def main():
    from collections import deque
    import sys

    input = sys.stdin.readline
    N, M = list(map(int, input().split()))
    g = tuple(set() for _ in range(N))
    for _ in range(M):
        x, y, _ = (int(x) - 1 for x in input().split())
        g[x].add(y)
        g[y].add(x)
    ans = 0
    checked = [False] * N
    for s in range(N):
        if checked[s]:
            continue
        checked[s] = True
        ans += 1
        dq = deque([s])
        while dq:
            v = dq.popleft()
            for u in g[v]:
                if checked[u]:
                    continue
                checked[u] = True
                dq.append(u)
    print(ans)


if __name__ == "__main__":
    main()

-import sys +def main(): +    from collections import deque +    import sys -input = sys.stdin.readline +    input = sys.stdin.readline +    N, M = list(map(int, input().split())) +    g = tuple(set() for _ in range(N)) +    for _ in range(M): +        x, y, _ = (int(x) - 1 for x in input().split()) +        g[x].add(y) +        g[y].add(x) +    ans = 0 +    checked = [False] * N +    for s in range(N): +        if checked[s]: +            continue +        checked[s] = True +        ans += 1 +        dq = deque([s]) +        while dq: +            v = dq.popleft() +            for u in g[v]: +                if checked[u]: +                    continue +                checked[u] = True +                dq.append(u) +    print(ans) -class UnionFind: -    def __init__(self, n): -        self.v = [-1 for _ in range(n)]  # 根(負): 連結頂点数 * (-1) / 子(正): 根の頂点番号(0-indexed) - -    def find(self, x):  # xを含む木における根の頂点番号を返す -        if self.v[x] < 0:  # (負)は根 -            return x -        else:  # 根の頂点番号 -            self.v[x] = self.find(self.v[x])  # uniteでは, 旧根に属する頂点の根が旧根のままなので更新 -            return self.v[x] - -    def unite(self, x, y):  # 違う根に属していたらrankが低くなるように連結 -        x = self.find(x) -        y = self.find(y) -        if x == y: -            return -        if -self.v[x] < -self.v[y]:  # size比較, 　(-1) * (連結頂点数 * (-1)), (正)同士の大小比較 -            x, y = y, x  # 連結頂点数が少ない方をyにすると, findでの更新回数が減る？ -        self.v[x] += self.v[y]  # 連結頂点数の和を取る, 連結頂点数 * (-1) -        self.v[y] = x  # 連結頂点数が少ないy(引数yの根の頂点番号)の根をx(引数xの根の頂点番号)にする - -    def root(self, x): -        return self.v[x] < 0  # (負)は根 - -    def same(self, x, y): -        return self.find(x) == self.find(y)  # 同じ根に属するか - -    def size(self, x): -        return -self.v[self.find(x)]  # 連結頂点数を返す - - -n, m = list(map(int, input().split())) -uf = UnionFind(n) -for _ in range(m): -    x, y, z = list(map(int, input().split())) -    x -= 1 -    y -= 1 -    uf.unite(x, y) -print((sum(uf.root(j) for j in range(n)))) +if __name__ == "__main__": +    main()