{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
import Control.Monad.ST
import Data.Array.ST
import Control.Arrow
import Control.Monad
import Data.List
import qualified Data.ByteString.Char8 as B

main :: IO ()
main = readLine >>= main'
    where
        main' [n, m] = readContents m >>= print . solve n . map (map pred)

solve :: Int -> [[Int]] -> Int
solve n es = length . filter id $ runST stRes
    where
        cs = build [] es

        stRes :: forall s. ST s [Bool]
        stRes = mapM stAction cs

        stAction :: [[Int]] -> forall s. ST s Bool
        stAction xs = makeTree n >>= connect xs >>= size 0 >>= check 

        build xs (y : ys) = (xs ++ ys) : build (y : xs) ys
        build _ _ = []

        connect xs uft = uft <$ mapM_ (\[a, b] -> unionNodes a b uft) xs 

        check s = return $ s /= n

                

readLine :: IO [Int]
readLine = unfoldr (fmap (second (maybe B.empty snd . B.uncons)) . B.readInt) <$> B.getLine

readContents :: Int -> IO [[Int]]
readContents = flip replicateM readLine

type UFTree s = STArray s Int (Int, Int)

makeTree :: Int -> ST s (UFTree s)
makeTree n = newListArray (0, n - 1) . zip [0..(n - 1)] $ repeat 1

findRoot :: Int -> UFTree s -> ST s (Int, Int)
findRoot v uft = readArray uft v >>= check
    where
        check res@(root, _)
            | v == root = return res
            | otherwise = findRoot root uft >>= edit
        edit res = res <$ writeArray uft v res
            
unionNodes :: Int -> Int -> UFTree s -> ST s ()
unionNodes a b uft = mapM (`findRoot` uft) [a, b] >>= check
    where
        check [(c, cs), (d, ds)]
            | c == d = return ()
            | cs < ds = writeArray uft c (d, cs + ds) >> writeArray uft d (d, ds + cs)
            | otherwise = writeArray uft d (c, cs + ds) >> writeArray uft c (c, cs + ds) 

isSame :: Int -> Int -> UFTree s -> ST s Bool
isSame a b uft = (== 1) . length . group <$> mapM (\v -> fst <$> findRoot v uft) [a, b]

size :: Int -> UFTree s -> ST s Int 
size v uft = snd <$> findRoot v uft