CodeEff/ECCO huggingface dataset:
# Original solution
n,m=list(map(int,input().split()))
a=[]
for i in range(m):
    x,y,z=list(map(int,input().split()))
    a.append((x-1,y-1,z))

class union:
    def __init__(self,num):
        self.par=[-1]*num
    def get_par(self,node):
        if self.par[node]==-1:
            return node
        else:
            x=self.get_par(self.par[node])
            return x
    def merge(self,x,y):
        X=self.get_par(x)
        Y=self.get_par(y)
        if X!=Y:
            self.par[Y]=X
    def output(self):
        print((self.par))
    def count(self):
        print((self.par.count(-1)))
u=union(n)
for i in range(m):
    u.merge(a[i][0],a[i][1])
u.count()


# Optimized solution
n,m=list(map(int,input().split()))
a=[]
for i in range(m):
    x,y,z=list(map(int,input().split()))
    a.append((x-1,y-1))

class union:
    def __init__(self,num):
        self.par=[-1]*num
    def get_par(self,node):
        if self.par[node]==-1:
            return node
        else:
            x=self.get_par(self.par[node])
            return x
    def merge(self,x,y):
        X=self.get_par(x)
        Y=self.get_par(y)
        if X!=Y:
            self.par[Y]=X
    def output(self):
        print((self.par))
    def count(self):
        print((self.par.count(-1)))
def get_parent(node):
    if par[node]==-1:
        return node
    else:
        par[node]=get_parent(par[node])
        return par[node]
    
def merge(x,y):
    i=get_parent(x)
    j=get_parent(y)
    if i!=j:
        par[i]=j
    return
par=[-1]*n
for i in range(m):
    merge(a[i][0],a[i][1])
print((par.count(-1)))
# Original solution
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


def main():
    N,M = list(map(int, input().split())) 
    uf = UnionFind(N)
    for _ in range(M):
        x, y, z = list(map(int, input().split())) 
        uf.union(x-1,y-1)

    print((uf.group_count()))


if __name__ == "__main__":
    main()

# Optimized solution
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())



def main():
    n,m = list(map(int, input().split()))
    uf = UnionFind(n)

    for _ in range(m):
        x,y,z = list(map(int, input().split()))
        # if z%2 == 0:
        uf.union(x-1, y-1)
    print((uf.group_count()))


if __name__ == "__main__":
    main()
# Original solution
N, M = list(map(int, input().split()))

relation = [[] for _ in range(N+1)]

for _ in range(M) :
	X, Y, Z = list(map(int, input().split()))
	relation[X].append(Y)
	relation[Y].append(X)

visited = [False] * (N + 1)

def dfs(init) :
	queue = [init]

	while queue :
		x_ = queue.pop(0)
		visited[x_] = True

		for y_ in relation[x_] :
			if not visited[y_] :
				queue.append(y_)

# 閉路ごとdfs実行
n_closed = 0
for i in range(1, N + 1) :
	if len(relation[i]) and not visited[i] :
		n_closed += 1
		dfs(i)

print((n_closed + visited.count(False) - 1))


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

parent = [-1] * (N + 1)

def root(x):
	while parent[x] >= 0:
		x = parent[x]
	return x

def unite(x,y):
	root_x = root(x)
	root_y = root(y)

	if root_x != root_y :
		if parent[root_x] > parent[root_y] :
			parent[root_x]=root_y
		else :
			if parent[root_x] == parent[root_y] :
				parent[root_x] -= 1
			parent[root_y] = root_x
    
	return

for _ in range(M) :
	X, Y, Z = list(map(int, input().split()))
	unite(X, Y)
	
print((sum(i < 0 for i in parent) - 1))
# Original solution
N, M = list(map(int, input().split()))
ans = [-1] * N

def find(x):
    global ans
    if ans[x] == -1:
        return x
    else:
        return find(ans[x])
    
for _ in range(M):
    x, y, z = list(map(int, input().split()))
    x -= 1
    y -= 1
    xx = find(x)
    yy = find(y)
    if ans[x] == ans[y] == -1:
        ans[y] = x
    elif xx == yy:
        continue
    else:
        ans[yy] = xx

n = 0
for i in ans:
    if i == -1:
        n += 1

print(n)

# Optimized solution
N, M = list(map(int, input().split()))
ans = [-1] * N

def find(x):
    global ans
    if ans[x] == -1:
        return x
    else:
        ans[x] = find(ans[x])
        return ans[x]
    
for _ in range(M):
    x, y, z = list(map(int, input().split()))
    x -= 1
    y -= 1
    xx = find(x)
    yy = find(y)
    if ans[x] == ans[y] == -1:
        ans[y] = x
    elif xx == yy:
        continue
    else:
        ans[yy] = xx

n = 0
for i in ans:
    if i == -1:
        n += 1

print(n)
# Original solution
N, M = list(map(int, input().split()))
ans = [-1] * N

def find(x):
    global ans
    if ans[x] == -1:
        return x
    else:
        ans[x] = find(ans[x])
        return ans[x]
    
for _ in range(M):
    x, y, z = list(map(int, input().split()))
    x -= 1
    y -= 1
    xx = find(x)
    yy = find(y)
    if ans[x] == ans[y] == -1:
        ans[y] = x
    elif xx == yy:
        continue
    else:
        ans[yy] = xx

n = 0
for i in ans:
    if i == -1:
        n += 1

print(n)

# Optimized solution
N, M = list(map(int, input().split()))
UF = [-1] * (N+1)
def find(x):
    global UF
    if UF[x] == -1:
        return x
    else:
        UF[x] = find(UF[x])
        return UF[x]
def union(x,y):
    global UF
    xx = find(x)
    yy = find(y)
    if UF[x] == UF[y] == -1:
        UF[y] = xx
        return
    elif xx == yy:
        return 
    else:
        UF[yy] = xx
        return 
    
for _ in range(M):
    x, y, z = list(map(int, input().split()))
    union(x,y)
    
print((sum([i < 0 for i in UF[1:]])))
# Original solution
from collections import deque

N, M = list(map(int, input().split()))
X = [ list(map(int, input().split())) for _ in range(M)]

Y = [ [] for _ in range(N)]
for x in X:
    Y[x[0]-1].append((x[0], x[1], x[2]))
    Y[x[1]-1].append((x[1], x[0], x[2]))
# print(Y)

visited = [False] * N
count = 0
que = deque()

for i in range(len(Y)):
    if len(Y[i]) == 0:
        visited[i] = True
        count +=1

while True:
    if not False in visited:
        break
    count += 1
    que.append(visited.index(False))
    
    while len(que)>0:
        v = que.popleft()
        visited[v] = True
        for y in Y[v]:
            if not visited[y[1]-1]:
                que.append(y[1]-1)
    

print(count)

# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n)]
        self.rank = [0 for _ in range(n)]
        
    def find_root(self, x):
        if self.par[x] == x:
            return x
        self.par[x] = self.find_root(self.par[x])
        return self.par[x]
    
    def union(self, x, y):
        x_root = self.find_root(x)
        y_root = self.find_root(y)
        if x_root != y_root:
            if self.rank[x_root] < self.rank[y_root]:
                x_root, y_root = y_root, x_root
            if self.rank[x_root] == self.rank[y_root]:
                self.rank[x_root] += 1
            self.par[y_root] = x_root
            
    def is_same(self, x, y):
        x_root = self.find_root(x)
        y_root = self.find_root(y)
        return x_root == y_root
        
N, M = list(map(int, input().split()))
X = [ list(map(int, input().split())) for _ in range(M)]

uf = UnionFind(N)
for x in X:
    uf.union(x[0]-1, x[1]-1)
    
roots = [ uf.find_root(i) for i in range(N)]
print((len(set(roots))))
# Original solution
def main():
    n,m = list(map(int,input().split()))
    graph = {}
    for i in range(n):
        graph[i+1] = []
    for i in range(m):
        x,y,z = list(map(int,input().split()))
        graph[x] += [y]
        graph[y] += [x]
    dis = [-1 for i in range (n)]
    ans = 0
    for i in range(n):
        if dis[i]==-1:
            ans += 1
            que = [i]
            dis[i] = 0
            while len(que)>0:
                s = que.pop(0)
                for nv in graph[s+1]:
                    if dis[nv-1]!=-1:
                        continue
                    dis[nv-1] = 0
                    que.append(nv-1)
    print(ans)
    
if __name__ == "__main__":
    main()


# Optimized solution
def main():
    n,m = list(map(int,input().split()))
    cards = [-1 for i in range(n)]
    mg = {}
    for i in range(n):
        mg[i+1] = []
    for i in range(m):
        x,y,z = list(map(int,input().split()))
        mg[x].append(y)
        mg[y].append(x)
    ans = 0
    for i in range(n):
        if cards[i]==-1:
            ans += 1
            cards[i] = 0
            que = [i]
            while len(que)>0:
                s = que.pop(0)
                for v in mg[s+1]:
                    if cards[v-1] == 0:
                        continue
                    cards[v-1] = 0
                    que.append(v-1)
    print(ans)

if __name__ == "__main__":
    main()
# Original solution
from collections import deque
N,M = list(map(int,input().split()))
E = [[] for _ in range(N+1)]
for i in range(M):
    x,y,z = list(map(int,input().split()))
    E[x].append(y)
    E[y].append(x)

d = [-1]*(N+1)

def dfs(start,num):
    q = deque([start])
    while(q):
        v = q.pop()
        if(d[v] == -1):
            d[v] = num
        for s in E[v]:
            if(d[s] == -1):
                q.append(s)

group_num = 0
for i in range(1,N+1):
    if(d[i] == -1):
        group_num += 1
        dfs(i,group_num)

print(group_num)    


# Optimized solution
from collections import deque
N,M = list(map(int,input().split()))
E = [[] for _ in range(N+1)]
for i in range(M):
    x,y,z = list(map(int,input().split()))
    E[x].append(y)
    E[y].append(x)

d = [-1]*(N+1)

def dfs(start,num):
    q = deque([start])
    d[start] = num
    while(q):
        v = q.pop()
        for s in E[v]:
            if(d[s] == -1):
                d[s] = num
                q.append(s)

group_num = 0
for i in range(1,N+1):
    if(d[i] == -1):
        group_num += 1
        dfs(i,group_num)

print(group_num)
# Original solution
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 * 2)
for _ in range(M):
    x, y, z = list(map(int, input().split()))
    x -= 1
    y -= 1
    if z % 2 == 0:
        uf.unite(x, y)
        uf.unite(x + N, y + N)
    else:
        uf.unite(x, y + N)
        uf.unite(x + N, y)

# print(uf.v)

ans = 0
for i in range(N):
    if uf.root(i):
        ans += 1
print(ans)


# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.v = [-1 for _ in range(n)]

    def find(self, x):
        if self.v[x] < 0:
            return x
        else:
            self.v[x] = self.find(self.v[x])
            return self.v[x]

    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if -self.v[x] < -self.v[y]:
            x, y = y, x
        self.v[x] += self.v[y]
        self.v[y] = 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 * 2)
for _ in range(M):
    x, y, z = list(map(int, input().split()))
    x -= 1
    y -= 1

    if z & 1:
        uf.unite(x + N, y)
        uf.unite(x, y + N)
    else:
        uf.unite(x, y)
        uf.unite(x + N, y + N)
print((sum(1 for i in range(N) if uf.root(i))))
# Original solution
class UnionFind:
    def __init__(self, n):
        self.v = [-1 for _ in range(n)]

    def find(self, x):
        if self.v[x] < 0:
            return x
        else:
            self.v[x] = self.find(self.v[x])
            return self.v[x]

    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if -self.v[x] < -self.v[y]:
            x, y = y, x
        self.v[x] += self.v[y]
        self.v[y] = 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 * 2)
for _ in range(M):
    x, y, z = list(map(int, input().split()))
    x -= 1
    y -= 1

    if z & 1:
        uf.unite(x + N, y)
        uf.unite(x, y + N)
    else:
        uf.unite(x, y)
        uf.unite(x + N, y + N)
print((sum(1 for i in range(N) if uf.root(i))))


# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.v = [-1 for _ in range(n)]

    def find(self, x):
        if self.v[x] < 0:
            return x
        else:
            self.v[x] = self.find(self.v[x])
            return self.v[x]

    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if -self.v[x] < -self.v[y]:
            x, y = y, x
        self.v[x] += self.v[y]
        self.v[y] = 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(1 for i in range(N) if uf.root(i))))
# Original solution
class UnionFind():
    def __init__(self, n):
        self.parent = [-1 for _ in range(n)]
        # 正==子: 根の頂点番号 / 負==根: 連結頂点数

    def find(self, x):
        if self.parent[x] < 0:
            return x
        else:
            self.parent[x] = self.find(self.parent[x])
            return self.parent[x]

    def unite(self, x, y):
        x, y = self.find(x), self.find(y)
        if x == y:
            return False
        else:
            if self.size(x) < self.size(y):
                x, y = y, x
            self.parent[x] += self.parent[y]
            self.parent[y] = x

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def size(self, x):
        x = self.find(x)
        return -self.parent[x]

    def is_root(self, x):
        return self.parent[x] < 0


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.is_root(i) for i in range(n))))


# Optimized solution
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))))
# Original solution
import sys
sys.setrecursionlimit(2*10**5)


N, M = list(map(int, input().split()))
X, Y, Z = (
    list(zip(*(list(map(int, input().split())) for _ in range(M)))) if M else
    ((), ())
)

G = [set() for _ in range(N + 1)]
for x, y in zip(X, Y):
    G[x].add(y)
    G[y].add(x)


dp = [-1 for _ in range(N + 1)]


def f(i, k):
    for t in G[i]:
        if dp[t] == -1:
            dp[t] = k
            f(t, k)


L = 0
for i in range(1, N + 1):
    if dp[i] == -1:
        L += 1
        f(i, L)
ans = L
print(ans)


# Optimized solution
# 入力
N, M = list(map(int, input().split()))
X, Y, Z = (
    list(zip(*(list(map(int, input().split())) for _ in range(M)))) if M else
    ((), (), ())
)


# UnionFind木で連結成分の個数を数える
class UnionFindTree:
    def __init__(self, n):
        self.p = [i for i in range(n + 1)]
        self.r = [0 for _ in range(n + 1)]

    def find(self, x):
        if self.p[x] != x:
            self.p[x] = self.find(self.p[x])
        return self.p[x]

    def union(self, x, y):
        px = self.find(x)
        py = self.find(y)
        if px != py:
            if self.r[px] < self.r[py]:
                self.p[px] = py
            else:
                self.p[py] = px
                if self.r[px] == self.r[py]:
                    self.r[px] += 1

    def same(self, x, y):
        return self.find(x) == self.find(y)


uft = UnionFindTree(N)
for x, y in zip(X, Y):
    uft.union(x, y)
ans = len({uft.find(i) for i in range(1, N + 1)})

# 出力
print(ans)
# Original solution
n,m=list(map(int,input().split()))

g = [[] for _ in range(n)]
for i in range(m):
	x,y,z=list(map(int,input().split()))
	g[x-1].append(y-1)
	g[y-1].append(x-1)

check = [False]*n

cnt=0
import queue
for i in range(n):
	if check[i]:
		continue
	cnt+=1
	check[i]=True
	q=queue.Queue()
	q.put(i)

	while not q.empty():
		now=q.get()
		for ne in g[now]:
			if check[ne]:
				continue
			check[ne]=True
			q.put(ne)

print(cnt)

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

g = [[] for _ in range(n)]
for i in range(m):
	x,y,z=list(map(int,input().split()))
	g[x-1].append(y-1)
	g[y-1].append(x-1)

check = [False]*n

cnt=0
import queue
q=queue.Queue()
for i in range(n):
	if check[i]:
		continue
	cnt+=1
	check[i]=True
	q.put(i)

	while not q.empty():
		now=q.get()
		for ne in g[now]:
			if check[ne]:
				continue
			check[ne]=True
			q.put(ne)

print(cnt)
# Original solution
n,m=list(map(int, input().split()))

tree=[[] for _ in range(n)]
for i in range(m):
    x,y,w = list(map(int,input().split()))
    tree[x-1].append(y-1)
    tree[y-1].append(x-1)

singles = [ i for i in range(n) if tree[i]==[]]

visited=[False]*n
for i in singles:
    visited[i] = True
ans=len(singles)

from collections import deque
while False in visited:
    ans+=1
    start = visited.index(False)
    visited[start]=True
    q=deque([(start,togo) for togo in tree[start]])
    while q:
        parent,x = q.pop()
        visited[x]=True
        for y in tree[x]:
            if not visited[y]:
                q.append((x,y))
#         print(parent,x,q)
#     print(visited,ans)
print(ans)

# Optimized solution
def main():
    import sys
    input = sys.stdin.readline
    n,m=list(map(int, input().split()))
    
    tree=[[] for _ in range(n)]
    for i in range(m):
        x,y,w = list(map(int,input().split()))
        tree[x-1].append(y-1)
        tree[y-1].append(x-1)
    
    singles = [ i for i in range(n) if tree[i]==[]]
    
    visited=[False]*n
    for i in singles:
        visited[i] = True
    ans=len(singles)
    
    from collections import deque
    while False in visited:
        ans+=1
        start = visited.index(False)
        visited[start]=True
        q=deque([(start,togo) for togo in tree[start]])
        while q:
            parent,x = q.pop()
            visited[x]=True
            for y in tree[x]:
                if not visited[y]:
                    q.append((x,y))
    #         print(parent,x,q)
    #     print(visited,ans)
    print(ans)
    
if __name__ == '__main__':
    main()
# Original solution
n,m=list(map(int,input().split()))

#初期値が0の辞書
from collections import defaultdict
xyz = defaultdict(lambda: set([]))
for _ in range(m):
    x,y,z=list(map(int,input().split()))
    xyz[x].add(y)
    xyz[y].add(x)

no_checked=[0]*n

cnt=0
for i in range(1,n+1):
    if no_checked[i-1]==0:
        cnt+=1
        temp=set([i])
        no_checked[i-1]=1
        while len(list(temp))>0:
            v=temp.pop()
            for item in xyz[v]:
                if no_checked[item-1]==0:
                    no_checked[item-1]=1
                    temp.add(item)
                    xyz[item].remove(v)
    #print(i,no_checked)
print(cnt)



# Optimized solution
import sys
sys.setrecursionlimit(1000000)

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

#初期値が0の辞書
from collections import defaultdict
xyz = defaultdict(lambda: set([]))
for _ in range(m):
    x,y,z=list(map(int,input().split()))
    xyz[x].add(y)
    xyz[y].add(x)

no_checked=[0]*n

def DFS(x):
    global no_checked
    global xyz
    no_checked[x-1]=1
    for item in xyz[x]:
        if no_checked[item-1]==0:
            DFS(item)

cnt=0
for i in range(1,n+1):
    if no_checked[i-1]==0:
        cnt+=1
        DFS(i)

    #print(i,no_checked)
print(cnt)