CodeEff/ECCO huggingface dataset:
# Original solution
#  --*-coding:utf-8-*--


def dsMakeSet(ds, x):
    ds[x] = x


def dsFind(ds, x):
    if ds[x] == x:
        return x

    while ds[x] != x:
        x = ds[x]

    ds[x] = x
    return x


def dsUnion(ds, x, y):
    xRoot = dsFind(ds, x)
    yRoot = dsFind(ds, y)
    
    if xRoot != yRoot:
        ds[yRoot] = xRoot
        return True

    return False


def main():
    N, M = list(map(int, input().split()))
    ds = {}

    for i in range(N):
        dsMakeSet(ds, i+1)

    n = 0
    for i in range(M):
        X,Y,Z = list(map(int, input().split()))
        if dsUnion(ds, X, Y):
            n += 1
        
    print((N - n))
        

if __name__ == '__main__':
    main()


# Optimized solution
#  --*-coding:utf-8-*--


def dsMakeSet(ds, x):
    ds[x] = x


def dsFind(ds, x):
    if ds[x] == x:
        return x

    x0 = x
    while ds[x] != x:
        x = ds[x]

    ds[x0] = x
    return x


def dsUnion(ds, x, y):
    xRoot = dsFind(ds, x)
    yRoot = dsFind(ds, y)
    
    if xRoot != yRoot:
        ds[yRoot] = xRoot
        return True

    return False


def main():
    N, M = list(map(int, input().split()))
    ds = {}

    for i in range(N):
        dsMakeSet(ds, i+1)

    n = 0
    for i in range(M):
        X,Y,Z = list(map(int, input().split()))
        if dsUnion(ds, X, Y):
            n += 1
        
    print((N - n))
        

if __name__ == '__main__':
    main()
# Original solution
import sys
sys.setrecursionlimit(10 ** 5)

class UniteFind:
    def __init__(self, N):
        self.par = [i for i in range(N)]

    def root(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.root(self.par[x])
            return self.par[x]

    def unite(self, x, y):
        rx = self.root(x)
        ry = self.root(y)
        if rx == ry:
            return
        self.par[ry] = rx

    def same(self, x, y):
        if (self.root(x) == self.root(y)):
            return True
        return False


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

for _ in range(M):
    x, y, _ = list(map(int, input().split()))
    rf.unite(x-1, y-1)

m = {i: -1 for i in range(N)}
ans = 0

for i in range(N):
    if m[rf.root(i)] == -1:
        m[rf.root(i)] = 1
        ans += 1

print(ans)


# Optimized solution
import sys
sys.setrecursionlimit(10 ** 5)

input = sys.stdin.readline

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

ans = list(range(N))
def root(a):
    if a==ans[a]:
        return a
    else:
        ans[a] = root(ans[a]) 
        return ans[a]

for _ in range(M):
    x, y, _ = [int(a)-1 for a in input().split()]
    rx, ry = root(x), root(y)
    if rx != ry:
        ans[ry] = rx

count = 0
for i,a in enumerate(ans):
    count += i==a


print(count)
# Original solution
import sys
input = sys.stdin.readline
'''
allinputs = iter(input().splitlines())
input = lambda : next(allinputs)
#'''

N, M = list(map(int,input().split()))
q = []
c = [0] * N
A = [[] for i in range(N)]

for i in range(M):
	x, y, z = list(map(int,input().split()))
	x = x - 1
	y = y - 1
	A[x].append(y)
	A[y].append(x)

cost = 0
for i in range(N):
	if c[i] == 0:
		cost += 1
		q.append(i)
		for _ in range(N):
			if len(q) == 0:
				break
			j = q.pop()
			c[j] = 1
			for k in A[j]:
				if c[k] == 0:
					q.append(k)

print(cost)


# Optimized solution
import sys
input = sys.stdin.readline
'''
allinputs = iter(input().splitlines())
input = lambda : next(allinputs)
#'''

N, M = list(map(int,input().split()))
q = []
c = [0] * N
A = [[] for i in range(N)]

for i in range(M):
	x, y, z = list(map(int,input().split()))
	x = x - 1
	y = y - 1
	A[x].append(y)
	A[y].append(x)

cost = 0
for i in range(N):
	if c[i] == 0:
		cost += 1
		q.append(i)
		while len(q):
			j = q.pop()
			c[j] = 1
			for k in A[j]:
				if c[k] == 0:
					q.append(k)

print(cost)
# Original solution
# input = sys.stdin.readline
from bisect import *
from collections import *
from heapq import *
# import functools
# import itertools
# import math
N,M=list(map(int,input().split()))
XYZ=[list(map(int,input().split())) for i in range(M)]
data=[[] for i in range(N+1)]
ittenai=[]
for x,y,z in XYZ:
    data[x].append(y)
    data[y].append(x)
    ittenai.append(x)
    ittenai.append(y)


ittenai=set(ittenai)
count=N-len(ittenai)
while ittenai:
    x=list(ittenai)[0]
    stack=deque([x])
    ittenai.remove(x)
    count+=1
    while stack:
        num=stack.popleft()
        for i in data[num]:
            if i in ittenai:
                ittenai.remove(i)
                stack.append(i)
print(count)


# Optimized solution
# input = sys.stdin.readline
from bisect import *
from collections import *
from heapq import *
# import functools
# import itertools
# import math
N,M=list(map(int,input().split()))
XYZ=[list(map(int,input().split())) for i in range(M)]
data=[[] for i in range(N+1)]
ittenai=[]
for x,y,z in XYZ:
    data[x].append(y)
    data[y].append(x)
    ittenai.append(x)
    ittenai.append(y)


ittenai=set(ittenai)
count=N-len(ittenai)
while ittenai:
    x=ittenai.pop()
    stack=deque([x])

    count+=1
    while stack:
        num=stack.popleft()
        for i in data[num]:
            if i in ittenai:
                ittenai.remove(i)
                stack.append(i)
print(count)
# Original solution
class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n+1)]
        self.rank = [0] * (n+1)

    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1

    def same_check(self, x, y):
        return self.find(x) == self.find(y)
n,m=list(map(int,input().split()))
uni = UnionFind(n-1)

for i in range(m):
    a,b,c=list(map(int,input().split()))
    a-=1
    b-=1
    uni.union(a,b)
t = set([])
for i in range(n):
    p = uni.find(i)
    t.add(p)
print((len(t)))

# Optimized solution
def main():
    #Union Find
    #locate x 
    def find(x):
        if par[x] < 0:
            return x
        else:
            par[x] = find(par[x])
            return par[x]
    #merge x y
    def unite(x,y):
        x = find(x)
        y = find(y)
        if x == y:
            return False
        else:
            #sizeの大きいほうがx
            if par[x] > par[y]:
                x,y = y,x
            par[x] += par[y]
            par[y] = x
            return True
    #x and y in same group
    def same(x,y):
        return find(x) == find(y)
    
    #number of factor of x group
    def size(x):
        return -par[find(x)]

    n,m = list(map(int,input().split()))
    #initiarize
    #根なら-size,子なら親の頂点
    par = [-1]*n

    for i in range(m):
        X,Y,Z = list(map(int,input().split()))
        unite(X-1,Y-1)
    
    tank = set([])
    for i in range(n):
        tank.add(find(i))
    print((len(tank)))

if __name__ == '__main__':
    main()
# Original solution
def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x])
        return par[x]
def unite(x,y):
    x = find(x)
    y = find(y)
    
    if x == y:
        return False
    else:
        if par[x] > par[y]:
            x,y = y,x
        par[x] += par[y]
        par[y] = x
        return True

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

def size(x):
    return -par[find(x)]

n,m = list(map(int,input().split()))
par = [-1]*n

for i in range(m):
    X,Y,Z = list(map(int,input().split()))
    unite(X-1,Y-1)

tank = set([])
for i in range(n):
    tank.add(find(i))
print((len(tank)))


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


par = [-1]*(n)
def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x]) #経路圧縮
        return par[x]
def same(x,y):
    return find(x) == find(y)
def unite(x,y):
    x = find(x)
    y = find(y)
    if x == y:
        return 0
    else:
      if par[x] > par[y]:x,y = y,x
      par[x] += par[y]
      par[y] = x
def size(x):
    return -par[find(x)]


for i in range(m):
  x,y,z = list(map(int,input().split()))
  unite(x-1,y-1)


ans = 0
# print(par)
for i in range(n):
  if par[i] <0:
    ans += 1
print(ans)
# Original solution
from collections import deque
    
N,M = list(map(int,input().split()))

node_list = [[] for i in range(N)]

for i in range(M):
    x,y,z=list(map(int,input().split()))
    node_list[x-1].append(y-1)
    node_list[y-1].append(x-1)

checked_list = [-1 for i in range(N)]

def BFS(start,color,checked_list,node_list):
    Q=deque([start])
    checked_list[start]=color
    while len(Q)>0:
        v1 = Q.pop()
        for v2 in node_list[v1]:
            if checked_list[v2]==-1:
                checked_list[v2]=color
                Q.appendleft(v2)


count = 0
for i in range(N):
    if checked_list[i]==-1:
        BFS(i,count,checked_list,node_list)
        count+=1
        
print(count)

# Optimized solution
import sys
def input():
    return sys.stdin.readline()[:-1]

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

e_list = [[] for i in range(N)]

for i in range(M):
    x,y,z = list(map(int,input().split()))
    x,y = x-1,y-1
    e_list[x].append(y)
    e_list[y].append(x)

from collections import deque

def BFS(vi,color,color_list):
    Q = deque([vi])
    color_list[vi]=color
    while len(Q)>0:
        v = Q.pop()
        for v1 in e_list[v]:
            if color_list[v1]==-1:
                color_list[v1]=color
                Q.appendleft(v1)

color_list = [-1]*N
color = 0

for i in range(N):
    if color_list[i]==-1:
        BFS(i,color,color_list)
        color+=1

print(color)
# Original solution
N, M = list(map(int, input().split()))
XYZ = [list(map(int, input().split())) for _ in range(M)]

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())
                     
uf = UnionFind(N)
for xyz in XYZ:
  x, y, z = xyz                       
  uf.union(x - 1, y - 1)
print((uf.group_count()))
                         

# 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())

n, m = list(map(int, input().split()))
XYZ = [list(map(int, input().split())) for _ in range(m)]

uf = uf = UnionFind(n)

for x, y, z in XYZ:
  x -= 1
  y -= 1
  uf.union(x, y)
  
print((uf.group_count()))
# Original solution
n,m=list(map(int,input().split()))

import sys
sys.setrecursionlimit(10**9) #再帰の上限をあげる

root=[-1 for i in range(n+1)] #自分が親ならグループの人数のマイナス倍を、そうでないなら（元）親の番号を示す

def r(x):   #親は誰？
    if root[x]<0:
        return x
    else:
        root[x]=r(root[x])
        return root[x]

def unite(x,y):
    x=r(x)
    y=r(y)
    if x==y:
        return
    if root[x]<root[y]:
        x,y=y,x
    root[x]+=root[y]
    root[y]=x

for i in range(m):
    x,y,z=list(map(int,input().split()))
    unite(x,y)


g=[0]*(n+1)
for i in range(1,n+1):
    g[r(i)]+=1
ans=0
for i in range(n+1):
    if g[i]>0:
        ans+=1
print(ans)



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

import sys
sys.setrecursionlimit(10**9) #再帰の上限をあげる

root=[-1 for i in range(n+1)] #自分が親ならグループの人数のマイナス倍を、そうでないなら（元）親の番号を示す

def r(x):   #親は誰？
    if root[x]<0:
        return x
    else:
        
        return r(root[x])

def unite(x,y):
    x=r(x)
    y=r(y)
    if x==y:
        return
    if root[x]>root[y]:
        x,y=y,x
    root[x]+=root[y]
    root[y]=x

for i in range(m):
    x,y,z=list(map(int,input().split()))
    unite(x,y)


g=[0]*(n+1)
for i in range(1,n+1):
    g[r(i)]+=1
ans=0
for i in range(n+1):
    if g[i]>0:
        ans+=1
print(ans)
# Original solution
import sys
sys.setrecursionlimit(10**8)
N, M = list(map(int, input().split()))
num_edges = M
edges = [list(map(int, input().split())) for i in range(num_edges)]
adj = [[] for i in range(N+1)]
for ed in edges:
    adj[ed[0]] += [[ed[1], ed[2]]]
    adj[ed[1]] += [[ed[0], ed[2]]]
col_list = [-1 for i in range(N+1)]

def recfun(v, col):
    col_list[v] = col #色を更新
    for ed in adj[v]: #隣接する頂点をみる
        if col_list[ed[0]] == -1: #更新していない場合
            recfun(ed[0], col) #同じ色でその先をみる
count = 0
for i in range(1, N+1):
    if col_list[i] == -1:
        count +=1
        recfun(i, count)
print(count)

# Optimized solution
class UnionFind():
    def __init__(self,size):
        self.table=[-1]*size
    def root(self,x):
        while self.table[x]>=0:
            x=self.table[x]
        return x
    def unite(self,x,y):
        s1=self.root(x)
        s2=self.root(y)
        if s1!=s2:
            if self.table[s1]>self.table[s2]:
                s1,s2=s2,s1
            self.table[s1]+=self.table[s2]
            self.table[s2]=s1
        return
    def same(self,x,y):
        return self.root(x)==self.root(y)
    def size(self,x):
        return -self.table[self.root(x)]
    
#####
N, M = list(map(int, input().split()))
uf = UnionFind(N+1)
for i in range(M):
    ui, vi, wi = list(map(int, input().split()))
    uf.unite(ui, vi)
print((sum([t < 0 for t in uf.table[1:]])))
# Original solution
import sys
sys.setrecursionlimit(10**6)

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


def dfs(num, depth):
    if c[num] != -1:
        return
    else:
        c[num] = depth
    for i in t[num]:
        dfs(i, depth)


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

print((max(c)))


# Optimized solution
import sys
sys.setrecursionlimit(10**6)

n, m = list(map(int, input().split()))
t = [[] for i in range(n)]
c = [-1 for i in range(n)]
for line in sys.stdin:
    x, y ,z = list(map(int, line.split()))
    x -= 1
    y -= 1
    t[x].append(y)
    t[y].append(x)


def dfs(num, depth):
    if c[num] != -1:
        return
    else:
        c[num] = depth
    for i in t[num]:
        dfs(i, depth)


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

print(cnt)
# Original solution



import sys
from collections import deque, defaultdict
import copy
import bisect
input=sys.stdin.readline
sys.setrecursionlimit(10 ** 9)
import math

N, M = list(map(int, input().split()))
XYZ = []
for i in range(M):
	XYZ.append(list(map(int, input().split())))

renketu = [dict() for i in range(N)]
for i in range(N):
	renketu[i]["iro"] = -1
	renketu[i]["eda"] = []
for i in range(M):
	renketu[XYZ[i][0] - 1]["eda"].append([XYZ[i][0], XYZ[i][1]])
	renketu[XYZ[i][1] - 1]["eda"].append([XYZ[i][1], XYZ[i][0]])

num = 0
for i in range(N):
	if renketu[i]['iro'] == -1:
		num += 1
		stack = [i]
		# print(rest, num)
		renketu[i]['iro'] = num
		while len(stack) > 0:
			node = stack.pop()
			for edge in renketu[node]["eda"]:
				if renketu[edge[1] - 1]["iro"] == -1:
					renketu[edge[1] - 1]["iro"] = num
					stack.append(edge[1] - 1)

print(num)

# Optimized solution


import sys
def input():
	return sys.stdin.readline().strip()

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


class Union_Find():
	def __init__(self, n):
		self.union = [i for i in range(n)]
		self.level = [0 for i in range(n)]
		self.num = n

	def root(self, i, mode=0):
		keiro = [i]
		ans = i
		while ans != self.union[ans]:
			ans = self.union[ans]
			keiro.append(ans)
		if mode == 0:
			return ans
		else:
			return ans, keiro

	def unite(self, i, j):
		root_i, list_i = self.root(i, 1)
		root_j, list_j = self.root(j, 1)
		if root_i != root_j:
			self.num -= 1
			if self.level[root_i] < self.level[root_j]:
				self.level[root_j] = max(self.level[root_i] + 1, self.level[root_j])
				for node in list_i:
					self.union[node] = root_j
			else:
				self.level[root_i] = max(self.level[root_j] + 1, self.level[root_i])
				for node in list_j:
					self.union[node] = root_i

union = Union_Find(N)

for i in range(M):
	X, Y, Z = list(map(int, input().split()))
	union.unite(X - 1, Y - 1)

print((union.num))
# Original solution
N,M=list(map(int, input().split()))
E=[[] for _ in range(N)]
for _ in range(M):
    x,y,z=[int(i)-1 for i in input().split()]
    E[x].append(y)
    E[y].append(x)

#情報のつながりがあるカードでグルーピングする
#グループIDのリスト(0は未割り当て)
G=[0]*(N+1)

def dfs(root,g):
    stack=[root]
    G[root]=g
    while stack:
        node=stack.pop()
        for to in E[node]:
            if G[to]==0:
                G[to]=g
                stack.append(to)

ans=0
for i in range(N):
    if G[i]==0:
        ans+=1
        dfs(i,ans)

print(ans)


# Optimized solution
import sys
def main():
    input = sys.stdin.readline
    N,M=list(map(int, input().split()))

    uf = UnionFindTree(N)
    for _ in range(M):
        x,y,z=list(map(int, input().split()))
        uf.union(x-1,y-1)
    c=set([uf.find(i) for i in range(N)])
    print((len(c)))


class UnionFindTree:
    def __init__(self, n):
        self.parent = [-1] * n

    def find(self, x):
        p = self.parent
        while p[x] >= 0: x, p[x] = p[x], p[p[x]]
        return x

    def union(self, x, y):
        x, y, p = self.find(x), self.find(y), self.parent
        if x == y: return
        if p[x] > p[y]: x, y = y, x
        p[x], p[y] = p[x] + p[y], x

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


if __name__ == '__main__':
    main()
# Original solution
# coding: utf-8
# Your code here!
class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))#親ノード
        self.size = [1]*n #グループの要素数

    def root(self, x): #root(x): xの根ノードを返す．
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x 

    def merge(self, x, y): #merge(x,y): xのいるグループと$y$のいるグループをまとめる
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return False
        if self.size[x] < self.size[y]: #xの要素数がyの要素数より「小さい」とき入れ替える
            x,y=y,x
        self.size[x] += self.size[y] #xの要素数を更新
        self.parent[y] = x #yをxにつなぐ
        return True

    def issame(self, x, y): #same(x,y): xとyが同じグループにあるならTrue
        return self.root(x) == self.root(y)
        
    def size(self,x): #size(x): xのいるグループの要素数を返す
        return self.size[self.root(x)]


n,m = [int(i) for i in input().split()]
 
T=UnionFind(n)
c=0
for _ in [0]*m:
    x,y,z=[int(i) for i in input().split()]
    if not T.issame(x-1,y-1):
        T.merge(x-1,y-1)
        c+=1
 
print((n-c))

# Optimized solution
# coding: utf-8
# Your code here!
class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))#親ノード
        self.size = [1]*n #グループの要素数

    def root(self, x): #root(x): xの根ノードを返す．
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x 

    def merge(self, x, y): #merge(x,y): xのいるグループと$y$のいるグループをまとめる
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return False
        if self.size[x] < self.size[y]: 
            self.size[y] += self.size[x] #yの要素数を更新
            self.parent[x] = y #xをyにつなぐ
        else:
            self.size[x] += self.size[y] #xの要素数を更新
            self.parent[y] = x #yをxにつなぐ
        return True

    def issame(self, x, y): #same(x,y): xとyが同じグループにあるならTrue
        return self.root(x) == self.root(y)
        
    def size(self,x): #size(x): xのいるグループの要素数を返す
        return self.size[self.root(x)]


n,m = [int(i) for i in input().split()]
 
T=UnionFind(n)
c=0
for _ in [0]*m:
    x,y,z=[int(i) for i in input().split()]
    if not T.issame(x-1,y-1):
        T.merge(x-1,y-1)
        c+=1
 
print((n-c))
# Original solution
# coding: utf-8
# Your code here!
class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))#親ノード
        self.size = [1]*n #グループの要素数

    def root(self, x): #root(x): xの根ノードを返す．
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x 

    def merge(self, x, y): #merge(x,y): xのいるグループと$y$のいるグループをまとめる
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return False
        if self.size[x] < self.size[y]: 
            self.size[y] += self.size[x] #yの要素数を更新
            self.parent[x] = y #xをyにつなぐ
        else:
            self.size[x] += self.size[y] #xの要素数を更新
            self.parent[y] = x #yをxにつなぐ
        return True

    def issame(self, x, y): #same(x,y): xとyが同じグループにあるならTrue
        return self.root(x) == self.root(y)
        
    def size(self,x): #size(x): xのいるグループの要素数を返す
        return self.size[self.root(x)]


n,m = [int(i) for i in input().split()]
 
T=UnionFind(n)
c=0
for _ in [0]*m:
    x,y,z=[int(i) for i in input().split()]
    if not T.issame(x-1,y-1):
        T.merge(x-1,y-1)
        c+=1
 
print((n-c))

# Optimized solution
# coding: utf-8
# Your code here!
class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))#親ノード
        self.size = [1]*n #グループの要素数

    def root(self, x): #root(x): xの根ノードを返す．
        while self.parent[x] != x:
            self.parent[x] = self.parent[self.parent[x]]
            x = self.parent[x]
        return x 

    def merge(self, x, y): #merge(x,y): xのいるグループと$y$のいるグループをまとめる
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return False
        if self.size[x] < self.size[y]: 
            self.size[y] += self.size[x] #yの要素数を更新
            self.parent[x] = y #xをyにつなぐ
        else:
            self.size[x] += self.size[y] #xの要素数を更新
            self.parent[y] = x #yをxにつなぐ
        return True

    def issame(self, x, y): #same(x,y): xとyが同じグループにあるならTrue
        return self.root(x) == self.root(y)
        
    def size(self,x): #size(x): xのいるグループの要素数を返す
        return self.size[self.root(x)]


n,m = [int(i) for i in input().split()]
 
T=UnionFind(n)
c=0
for _ in [0]*m:
    x,y,z=[int(i) for i in input().split()]
    if T.merge(x-1,y-1):
        c+=1
 
print((n-c))