import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.IO as T
import qualified Data.Text.Lazy.Read as T
import Data.IntMap.Strict (IntMap)
import qualified Data.IntMap.Strict as IMS
import Data.Bool (bool)

main :: IO ()
main = do
  let
    f :: [(Int, Int)] -> UF
    f = foldl step IMS.empty
      where
        step :: UF -> (Int, Int) -> UF
        step = ((fst .) .) . flip . uncurry $ unite
  [n, m] <- map unsafeTextToInt . T.words <$> T.getLine :: IO [Int]
  let
    g :: UF -> Int
    g u = foldl step 0 [0 .. (n - 1)]
      where
        step :: Int -> Int -> Int
        step c x | snd (root x u) == x = c + 1
                 | otherwise           = c
  abs <- map (unsafeListToPair . map (pred . unsafeTextToInt) . T.words) . T.lines <$> T.getContents :: IO [(Int, Int)]
  print . pred . g . f $ abs

type UF = IntMap Int

parent :: Int -> UF -> Maybe Int
parent = IMS.lookup

root :: Int -> UF -> (UF, Int)
root i u = maybe (u, i) (\j -> let (u', j') = root j u in (bool (IMS.insert i j' u') u (j' == j), j')) (parent i u)

unite :: Int -> Int -> UF -> (UF, ())
unite i j u | i' == j'  = (u'', ())
            | otherwise = (IMS.insert j' i' u'', ())
  where
    (u', i') = root i u
    (u'', j') = root j u'

isSame :: Int -> Int -> UF -> (UF, Bool)
isSame i j u = (u'', i' == j')
  where
    (u', i') = root i u
    (u'', j') = root j u'

unsafeListToPair :: [Int] -> (Int, Int)
unsafeListToPair = (,) <$> head <*> head . tail
{-# INLINE unsafeListToPair #-}

unsafeTextToInt :: T.Text -> Int
unsafeTextToInt s = case T.signed T.decimal s of
  Right (n, _) -> n
{-# INLINE unsafeTextToInt #-}
