def get_i()
  return gets.chomp.split(" ").map(&:to_i)
end
#input of float(split by space)
def get_f()
  return gets.chomp.split(" ").map(&:to_f)
end
#input of string(split by space)
def get()
  return gets.chomp.split(" ")
end
#input of string(split per one character)
def get_nsp()
  return gets.chomp.split("")
end
#yes or no decision
def yn_judge(bool,y="Yes",n="No")
  return bool ? y : n 
end
#create of array
def array(size1,init=nil,size2=-1)
  if size2==-1
      return Array.new(size1){init}
  else
      return Array.new(size2){Array.new(size1){init}}
  end
end

def max(a,b)
    return a>b ? a : b
end

def min(a,b)
    return a>b ? b : a
end

INF=Float::INFINITY


#====UnionFindTree====
class UnionFindTree
    attr_accessor:parent,:size,:rank,:group
    def initialize(n)
        @parent=Array.new(n){|idx| idx}
        @rank=Array.new(n,0)
        @size=Array.new(n,1)
    end

    def find(x)
        return x if @parent[x]==x
        @parent[x]=find(@parent[x])
        return @parent[x]
    end

    def union(x,y)
        par_x=find(x)
        par_y=find(y)
        return if par_x==par_y
        if @rank[par_x]<@rank[par_y]
            @parent[par_x]=par_y
            @size[par_y]+=@size[par_x]
        else
            @parent[par_y]=par_x
            @size[par_x]+=@size[par_y]
            @rank[par_x]+=1 if @rank[par_x]==@rank[par_y]
        end
    end

    def same?(x,y)
        return find(x)==find(y)
    end

    def get_size(x)
        return @size[find(x)]
    end
    def make_groups()
      n=@parent.size
      @group=Array.new(n){ Array.new }
      @parent.each_with_index do|par,i|
        @group[find(par)]<< i
      end
    end
end


N,M=get_i
uft=UnionFindTree.new(N)
M.times do|i|
  a,b=get_i.map{|x| x-=1}
  uft.union(a,b)
end
uft.make_groups
ans=N
N.times do|i|
    ans-=1 unless uft.group[i]==[]
end
puts ans