{-# LANGUAGE OverloadedStrings #-}
import           Control.Monad
import qualified Data.Vector.Unboxed         as VU
import qualified Data.Vector.Unboxed.Mutable as VUM

import qualified Control.Arrow               as Arrow
import           Data.Bool
import qualified Data.ByteString.Char8       as BSC8

type UnionFind = VUM.IOVector Int

newUF :: Int -> IO UnionFind
newUF n = VUM.replicate n (-1)

rootUF :: UnionFind -> Int -> IO Int
rootUF uf x = do
  p <- VUM.read uf x
  if p < 0 then
    return x
  else do
    r <- rootUF uf p
    VUM.write uf x r
    return r

connectedUF :: UnionFind -> Int -> Int -> IO Bool
connectedUF uf x y = liftM2 (==) (rootUF uf x) (rootUF uf y)

uniteUF :: UnionFind -> Int -> Int -> IO ()
uniteUF uf x y = do
  a <- rootUF uf x
  b <- rootUF uf y
  when (a /= b) $ do
    ar <- VUM.read uf a
    br <- VUM.read uf b
    let (p, c) = if ar < br then (a,b) else (b,a)
    when (ar == br) $ VUM.modify uf pred p
    VUM.write uf c p



type Parser a = BSC8.ByteString -> Maybe (a, BSC8.ByteString)
parseInt :: Parser Int
parseInt = fmap (Arrow.second BSC8.tail) . BSC8.readInt
parse2 :: IO (Int, Int)
parse2 = (\vec -> (vec VU.! 0, vec VU.! 1)) . VU.unfoldrN 2 parseInt <$> BSC8.getLine
parse3 :: IO (Int, Int, Int)
parse3 = (\vec -> (vec VU.! 0, vec VU.! 1, vec VU.! 2)) . VU.unfoldrN 3 parseInt <$> BSC8.getLine

main :: IO ()
main = do
  (n, q) <- parse2
  uf <- newUF n
  replicateM_ q $ do
    (t, u, v) <- parse3
    case t of
      0 -> uniteUF uf u v
      1 -> connectedUF uf u v >>= print . bool 0 1
