CodeEff/ECCO huggingface dataset:
# Original solution
N,M=list(map(int,input().split()))

class UnionFind():
  def __init__(self,n):
    self.parent={}
    for i in range(n):
      self.parent[i]=[i]
      
  def find_parent(self,x):
    import sys
    parent=self.parent
    for key in list(parent.keys()):
      if x in parent[key]:
        return key
        sys.exit()
        
  def union(self,x,y):
    parent=self.parent
    x_par,y_par=self.find_parent(x),self.find_parent(y)
    if x_par==y_par:
      pass
    else:
      if len(parent[x_par])<len(parent[y_par]):
        parent[y_par]+=parent[x_par]
        parent.pop(x_par)
      else:
        parent[x_par]+=parent[y_par]
        parent.pop(y_par)
    self.parent=parent
    return None
  
  def same(self,x,y):
    if self.find_parent(x)==self.find_parent(y):
      return True
    else:
      return False
  def cnt(self):
    par=self.parent
    return len(set(par))
uf=UnionFind(N)
for i in range(M):
  x,y,z=list(map(int,input().split()))
  uf.union(x-1,y-1)
print((uf.cnt()))

# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.n = n
        self.p = [e for e in range(n)]
        self.rank = [0] * n
        self.size = [1] * n

    def same(self, u, v):
        return self.find_set(u) == self.find_set(v)

    def unite(self, u, v):
        u = self.find_set(u)
        v = self.find_set(v)

        if u == v:
            return

        if self.rank[u] > self.rank[v]:
            self.p[v] = u
            self.size[u] += self.size[v]
        else:
            self.p[u] = v
            self.size[v] += self.size[u]
            if self.rank[u] == self.rank[v]:
                self.rank[v] += 1

    def find_set(self, u):
        if u != self.p[u]:
            self.p[u] = self.find_set(self.p[u])

        return self.p[u]

    def update_p(self):
        for u in range(self.n):
            self.find_set(u)

    def get_size(self, u):
        return self.size[self.find_set(u)]


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

uf = UnionFind(n)

for x, y, _ in xyz:
    x -= 1
    y -= 1
    uf.unite(x, y)

uf.update_p()
ans = len(set(uf.p))
print(ans)
# Original solution
import queue
import sys
sys.setrecursionlimit(10**6)

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

G=[[] for _ in range(N+1)]
for el in XYZ:
    G[el[0]].append(el[1])
    G[el[1]].append(el[0])
#print(G)

seen=[False]*(N+1)
todo=queue.Queue()
def dfs(n):
    seen[n]=True
    for nxt in G[n]:
        if seen[nxt]==False:
            todo.put(nxt)
    if todo.empty():
        return None
    else:
        dfs(todo.get())


cnt=0
for check in range(1, N+1):
    if seen[check]==False:
        cnt+=1
        dfs(check)
print(cnt)


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

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

G=[[] for _ in range(N+1)]
for el in XYZ:
    G[el[0]].append(el[1])
    G[el[1]].append(el[0])
#print(G)

seen=[False]*(N+1)
todo=queue.Queue()
def bfs(n):
    seen[n]=True
    for nxt in G[n]:
        if seen[nxt]==False:
            bfs(nxt)


cnt=0
for check in range(1, N+1):
    if seen[check]==False:
        cnt+=1
        bfs(check)
print(cnt)
# Original solution
N,M=list(map(int,input().split()))
root=[-1]*N
def search(i):
    if root[i]>=0:
        root[i]=search(root[i])
        return root[i]
    return i
for i in range(M):
    x,y,z=list(map(int,input().split()))
    x-=1
    y-=1
    rx=search(x)
    ry=search(y)
    if rx==ry:
        continue
    elif root[rx]<=root[ry]:
        root[ry]+=root[rx]
        root[rx]=ry
    else:
        root[rx]+=root[ry]
        root[ry]=rx
ans=0
for i in range(N):
    if root[i]<0:
        ans+=1
print(ans)


# Optimized solution
N,M=list(map(int,input().split()))
root=[-1]*N
def find(i):
    global root
    if root[i]>=0:
        root[i]=find(root[i])
        return root[i]
    return i
for i in range(M):
    X,Y,Z=list(map(int,input().split()))
    rx=find(X-1)
    ry=find(Y-1)
    if rx==ry:
        continue
    elif root[rx]<=root[ry]:
        root[rx]+=root[ry]
        root[ry]=rx
    elif root[rx]>root[ry]:
        root[ry]+=root[rx]
        root[rx]=ry
ans=0
for r in root:
    if r<0:
        ans+=1
print(ans)
# Original solution
n, m = list(map(int, input().split()))

xy = [i for i in range(n + 1)]


def root(x):
    i = x
    a = []
    while i != xy[i]:
        i = xy[i]
        a += [i]
    for b in a:
        xy[b] = i
    return i


for _ in range(m):
    x, y = list(map(int, input().split()[:2]))
    xy[root(x)] = root(y)

ans = sum(i == v for i, v in enumerate(xy)) - 1

print(ans)


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

xy = [i for i in range(n + 1)]


def root(x):
    i = x
    while i != xy[i]:
        i = xy[i]
    xy[x] = i
    return i


for _ in range(m):
    x, y = list(map(int, input().split()[:2]))
    xy[root(x)] = root(y)

print((sum(i == v for i, v in enumerate(xy)) - 1))
# Original solution
#!/usr/bin/env python3
#abc126 e

import sys
sys.setrecursionlimit(100000)
N,M = list(map(int,input().split()))
XYZ = [list(map(int,input().split())) for _ in range(M)]

G = [[] for _ in range(N)]

for i in XYZ:
    G[i[0]-1].append(i[1])
    G[i[1]-1].append(i[0])

check = [False for _ in range(N)]#各頂点を訪問したかどうか

def DFS(G,s):
    check[s-1] = True
    for i in G[s-1]:
        if not check[i-1]:
            DFS(G,i)
ans = 0
for i in range(N):
    if not check[i]:
        DFS(G,i+1)
        ans += 1
print(ans)


# Optimized solution
#!/usr/bin/env python3
#abc126 e

import sys
sys.setrecursionlimit(1000000)
N,M = list(map(int,input().split()))
XYZ = [list(map(int,input().split())) for _ in range(M)]

G = [[] for _ in range(N)]

for i in XYZ:
    G[i[0]-1].append(i[1])
    G[i[1]-1].append(i[0])

check = [False for _ in range(N)]#各頂点を訪問したかどうか

def DFS(G,s):
    check[s-1] = True
    for i in G[s-1]:
        if not check[i-1]:
            DFS(G,i)
ans = 0
for i in range(N):
    if not check[i]:
        DFS(G,i+1)
        ans += 1
print(ans)
# Original solution
import sys;sys.setrecursionlimit(10**9)
class u():#UnionFind f-根探し u-連結 s-連結? c-要素数
  def __init__(self,n):self.n,self.r=[-1]*n,[0]*n
  def f(self,x):
    if self.n[x]<0:return x
    else:self.n[x]=self.f(self.n[x]);return self.n[x]
  def u(self,x,y):
    x,y=self.f(x),self.f(y)
    if x==y:return
    elif self.r[x]>self.r[y]:self.n[x]+=self.n[y];self.n[y]=x
    else:self.n[y]+=self.n[x];self.n[x]=y;self.r[y]+=self.r[x]==self.r[y]
  def s(self,x,y):return self.f(x)==self.f(y)
  def c(self,x):return -self.n[self.f(x)]
n,m=list(map(int,input().split()));u=u(n)
for i in range(m):
  a,b,c=list(map(int,input().split()));a-=1;b-=1
  if not(u.s(a,b)):n-=1;u.u(a,b)
print(n)

# Optimized solution
import sys;sys.setrecursionlimit(10**9)
class UnionFind:
  def __init__(self,n):
    self.n=[-1]*n
    self.r=[0]*n
    self.siz=n
  def find_root(self,x):
    if self.n[x]<0:
      return x
    else:
      self.n[x]=self.find_root(self.n[x])
      return self.n[x]
  def unite(self,x,y):
    x=self.find_root(x)
    y=self.find_root(y)
    if x==y:return
    elif self.r[x]>self.r[y]:
      self.n[x]+=self.n[y]
      self.n[y]=x
    else:
      self.n[y]+=self.n[x]
      self.n[x]=y
      if self.r[x]==self.r[y]:
        self.r[y]+=1
    self.siz-=1
  def root_same(self,x,y):
    return self.find_root(x)==self.find_root(y)
  def count(self,x):
    return -self.n[self.find_root(x)]
  def size(self):
    return self.siz

n,m=list(map(int,input().split()))
uf=UnionFind(n)
for i in range(m):
  x,y,z=list(map(int,input().split()))
  uf.unite(x-1,y-1)
print((uf.size()))
# Original solution
# -*- coding: utf-8 -*-

import sys
sys.setrecursionlimit(10 ** 6)

def dfs(n):
    # print('Search Start: ' + str(n))
    if checked[n]:
        # print('Checked: ' + str(n))
        return
    checked[n] = True
    for e in edge:
        if n in e:
            x, y = e
            if n != x:
                dfs(x)
            else:
                dfs(y)
    # print('Search End: ' + str(n))

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

edge = [] * M
checked = [False for _ in range(N)]

con_com = 0

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

for i in range(N):
    if checked[i]:
        continue
    dfs(i)
    # print('--- Connected Components ---')
    con_com += 1

print(con_com)


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

import sys
sys.setrecursionlimit(10 ** 6)

class UnionFind(object):
    def __init__(self, n):
        self.parent = [i for i in range(n)]
        self.rank = [0 for _ in range(n)]
        self.size = [1 for _ in range(n)]
        self.components = n

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

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                x, y = y, x
            elif self.rank[x] == self.rank[y]:
                self.rank[x] += 1
            self.parent[y] = x
            self.size[x] += self.size[y]
            self.components -= 1

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

    def getSize(self, x):
        return self.size(self.find(x))

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

edge = [] * M
uf = UnionFind(N)

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

print((uf.components))
# Original solution
from collections import deque

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

connects = [0]*(n+1)
group = 1
for i in range(1, n+1):
    if connects[i] > 0:
        continue
    if len(xyz[i]) == 0:
        connects[i] = group
        group += 1
        continue

    que = deque(xyz[i])
    connects[i] = group
    while len(que) > 0:
        x, y, z = que.popleft()
        connects[y] = group
        for xn, yn, zn in xyz[y]:
            if connects[yn] > 0:
                pass
            else:
                que.append((xn, yn, zn))
    group += 1
    
# print(connects)
print((max(connects)))


# Optimized solution
from collections import deque

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


uf = [-1]*(n+1)
def find(i):
    if uf[i] < 0:
        return i
    else:
        uf[i] = find(uf[i])
        return uf[i]

for x, y, z in xyz:
    px, py = find(x), find(y)
    
    if px != py:
        px, py = (px, py) if uf[py] < uf[px] else (px, py)
        uf[px] += uf[py]
        uf[py] = px

# print(uf)
ans = sum([1 for i in range(1, n+1) if uf[i] < 0])
print(ans)
# Original solution
from collections import deque

n, m = list(map(int, input().split()))
xyz = [[] for i in range(n+1)]
for _ in range(m):
    x, y, z = list(map(int, input().split()))
    xyz[x].append(y)
    xyz[y].append(x)
    
ans = 0
visited = [False]*(n+1)
for x in range(1, n+1):
    if visited[x]:
        continue

    que = deque(xyz[x])
    while len(que) > 0:
        y = que.popleft()
        visited[y] = True
        for yn in xyz[y]:
            if not visited[yn]:
                que.append(yn)
    ans += 1
    
# print(visited)
print(ans)


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

n, m = list(map(int, input().split()))
xyz = [[] for i in range(n+1)]
for _ in range(m):
    x, y, z = list(map(int, input().split()))
    xyz[x].append(y)
    xyz[y].append(x)
    
ans = 0
visited = [False]*(n+1)
def dfs(x):
    visited[x] = True
    for y in xyz[x]:
        if not visited[y]:
            dfs(y)

for x in range(1, n+1):
    if visited[x]:
        continue
    dfs(x)
    ans += 1
    
# print(visited)
print(ans)
# Original solution
N, M = list(map(int, input().split()))

G = [set([n]) for n in range(N)]

for m in range(M):
    X, Y, Z = list(map(int, input().split()))
    X   -= 1
    Y   -= 1
    Gx   = G[X]
    Gy   = G[Y]
    Gxy  = Gx | Gy
    for idx in Gxy:
        G[idx] = Gxy

print((len(set([tuple(g) for g in G]))))


# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.depth  = [0] * n
        self.parent = list(range(n))

    def find(self, x):
        if x == self.parent[x]:
            return x
        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
        if self.depth[x] < self.depth[y]:
            self.parent[x] = y
        else:
            self.parent[y] = x
            if self.depth[x] == self.depth[y]:
                self.depth[x] += 1

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

    def size(self, x):
        return self.depth[self.ind(x)]


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

T = UnionFind(N)

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

print((sum([1 for i in range(N) if T.find(i) == i])))
# Original solution
import sys
input = sys.stdin.readline
 
n,m = list(map(int,input().split()))
 
#Union Find
par = [] #親
rank = [] #木の深さ
 
#初期化
for i in range(n):
    #par[i]:i rank[i]:0
    par.append(i)
    rank.append(0)
 
#木の根を求める
def find(x,par):
    if par[x] == x:
        return x
    else:
        return find(par[x],par)
 
#xとyの属する集合の併合
def unite(x,y,par,rank):
    x = find(x,par)
    y = find(y,par)
    
    if x != y:
        #xとyの属している集合が異なる時
        if rank[x] < rank[y]:
            par[x] = y
        else:
            par[y] = x
            if rank[x]==rank[y]:
                rank[x] += 1
 
#xとyが同じ集合に属するかの判定
def same(x,y,par):
    return find(x,par) == find(y,par)
 
for i in range(m):
    X,Y,Z = list(map(int,input().split()))
    unite(X-1,Y-1,par,rank)
 
tank = []
for i in range(n):
    p = find(i,par)
    tank.append(p)
print((len(set(tank))))

# Optimized solution
#Union Find
import sys
input = sys.stdin.readline
 
n,m = list(map(int,input().split()))

par = [i for i in range(n)] #親
rank = [0]*n #木の深さ

#木の根を求める
def find(x,par):
    if par[x] == x:
        return x
    else:
        par[x] = par[par[x]]
        return find(par[x],par)

#xとyの属する集合の併合
def unite(x,y,par,rank):
    x = find(x,par)
    y = find(y,par)
    
    if x != y:
        #xとyの属している集合が異なる時
        if rank[x] < rank[y]:
            par[x] = y
        else:
            par[y] = x
            if rank[x]==rank[y]:
                rank[x] += 1

#xとyが同じ集合に属するかの判定
def same(x,y,par):
    return find(x,par) == find(y,par)

for i in range(m):
    X,Y,Z = list(map(int,input().split()))
    unite(X-1,Y-1,par,rank)
 
tank = set([])
for i in range(n):
    p = find(i,par)
    tank.add(p)
print((len(tank)))
# Original solution
#Union Find
import sys
input = sys.stdin.readline
 
n,m = list(map(int,input().split()))

#根なら-size,子なら親の頂点
par = [-1]*n

#xの根を求める
def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x])
        return par[x]
#xとyの属する集合の併合
def unite(x,y):
    x = find(x)
    y = find(y)
    
    if x != y:
        #sizeの大きいほうがx
        if par[x] > par[y]:
            x,y = y,x
        par[x] += par[y]
        par[y] = x

#xとyが同じ集合に属するかの判定
def same(x,y):
    return find(x) == find(y)

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)
 
tank = set([])
for i in range(n):
    p = find(i)
    tank.add(p)
print((len(tank)))

# Optimized solution
def main():
    import sys
    input = sys.stdin.readline


    #Union Find

    #xの根を求める
    def find(x):
        if par[x] < 0:
            return x
        else:
            par[x] = find(par[x])
            return par[x]
    #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とyが同じ集合に属するかの判定
    def same(x,y):
        return find(x) == find(y)

    #xが属する集合の個数
    def size(x):
        return -par[find(x)]

    n,m = list(map(int,input().split()))
    #初期化
    #根なら-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
# Union-Findデータ構造
class UnionFind:
    def __init__(self, numV):
        self.pars = list(range(numV))
        self.ranks = [0] * numV
    def find(self, x):
        if self.pars[x] == x: return x
        else:
            self.pars[x] = self.find(self.pars[x])
            return self.pars[x]
    def union(self, x, y):
        x, y = self.find(x), self.find(y)
        if x == y: return
        if self.ranks[x] < self.ranks[y]:
            self.pars[x] = y
        else:
            self.pars[y] = x
            if self.ranks[x] == self.ranks[y]:
                self.ranks[x] += 1
    def same(self, x, y):
        return self.find(x) == self.find(y)


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)

for i in range(N):
    UF.find(i)

parents = UF.pars
print((len(set(parents))))


# Optimized solution
class UnionFind:
    def __init__(self, numV):
        self.pars = list(range(numV))
        self.ranks = [0] * numV
    def getRoot(self, x):
        par = self.pars[x]
        if par != x:
            self.pars[x] = par = self.getRoot(par)
        return par
    def merge(self, x, y):
        x, y = self.getRoot(x), self.getRoot(y)
        if x == y: return
        if self.ranks[x] < self.ranks[y]:
            self.pars[x] = y
        else:
            self.pars[y] = x
            if self.ranks[x] == self.ranks[y]:
                self.ranks[x] += 1
    def isSame(self, x, y):
        return self.getRoot(x) == self.getRoot(y)
    def updatePars(self):
        for v in range(len(self.pars)):
            self.getRoot(v)

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

UF = UnionFind(N)
for _ in range(M):
    x, y, z = list(map(int, input().split()))
    x, y = x-1, y-1
    UF.merge(x, y)

UF.updatePars()

ans = len(set(UF.pars))
print(ans)
# Original solution
from queue import Queue

N, M = list(map(int, input().split()))
d = {}
for i in range(1, N+1):
    d[i] = []
for i in range(M):
    x, y, z = list(map(int, input().split()))
    d[x].append(y)
    d[y].append(x)

isChecked = [False for i in range(N)]
ans = 0
for k in d:
    if isChecked[k-1]:
        continue
    ans += 1
    q = Queue()
    q.put(k)
    while not q.empty():
        v = q.get()
        if isChecked[v-1]:
            continue
        isChecked[v-1] = True
        for u in d[v]:
            q.put(u)

print(ans)


# Optimized solution
class UnionFindTree:
    _node_num = 0
    _parent_list = []
    _tree_len = []
    _num_trees = 0

    def __init__(self, node_num):
        self._node_num = node_num
        self._parent_list = [-1 for i in range(node_num+1)]
        self._tree_len = [0 for i in range(node_num+1)]
        self._num_trees = node_num

    def _root(self, node):
        root = node
        while self._parent_list[root] != -1:
            root = self._parent_list[root]
        return root

    def union(self, x, y):
        xr = self._root(x)
        yr = self._root(y)
        if xr == yr:
            return
        if self._tree_len[xr] < self._tree_len[yr]:
            self._parent_list[xr] = yr
            self._tree_len[yr] += 1
        else:
            self._parent_list[yr] = xr
            self._tree_len[xr] += 1
        self._num_trees -= 1

    def get_num_trees(self):
        return self._num_trees

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

    uft = UnionFindTree(N)
    for i in range(M):
        x, y, z = list(map(int, input().split()))
        uft.union(x, y)
    print((uft.get_num_trees()))

if __name__ == '__main__':
    main()
# Original solution
# Union Find 木
from sys import setrecursionlimit


def find(parent, i):
    t = parent[i]
    if t < 0:
        return i
    t = find(parent, t)
    parent[i] = t
    return t


def unite(parent, i, j):
    i = find(parent, i)
    j = find(parent, j)
    if i == j:
        return
    parent[j] += parent[i]
    parent[i] = j


setrecursionlimit(10 ** 5)

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

parent = [-1] * N
for _ in range(M):
    X, Y, Z = list(map(int, input().split()))
    unite(parent, X - 1, Y - 1)
print((len([x for x in parent if x < 0])))


# Optimized solution
# Union Find 木
from sys import setrecursionlimit


def find(parent, i):
    t = parent[i]
    if t < 0:
        return i
    t = find(parent, t)
    parent[i] = t
    return t


def unite(parent, i, j):
    i = find(parent, i)
    j = find(parent, j)
    if i == j:
        return
    parent[j] += parent[i]
    parent[i] = j


setrecursionlimit(10 ** 6)

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

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

#Union-Find
par=[-1 for i in range(n)]

def root(a):
    if par[a]<0:
        return a
    else:
        return root(par[a])

def size(a):
    return -par[root(a)]

def connect(a,b):
    a=root(a)
    b=root(b)
    if a==b:
        return False
    if size(a)<size(b):
        a,b=b,a
    par[a]+=par[b]
    par[b]=a
    return True

xyz=[]
for i in range(m):
    a=[int(j) for j in input().split()]
    xyz.append([a[0]-1,a[1]-1,a[2]%2])
    if root(a[0]-1)!=root(a[1]-1):
        connect(a[0]-1,a[1]-1)
ans=0
for i in range(n):
    ans+=1/size(i)
print((round(ans)))

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

#Union-Find
par=[-1 for i in range(n)]

def root(a):
    if par[a]<0:
        return a
    else:
        return root(par[a])

def size(a):
    return -par[root(a)]

def connect(a,b):
    a=root(a)
    b=root(b)
    if a==b:
        return False
    if size(a)<size(b):
        a,b=b,a
    par[a]+=par[b]
    par[b]=a
    return True

xyz=[]
for i in range(m):
    a=[int(j) for j in input().split()]
    xyz.append([a[0]-1,a[1]-1,a[2]%2])
    if root(a[0]-1)!=root(a[1]-1):
        connect(a[0]-1,a[1]-1)
ans=0
for i in range(n):
    if par[i]<0:
        ans+=1
print(ans)