Problem:
Score : 500 points

Problem Statement

We have N integers. The i-th number is A_i.

\{A_i\} is said to be pairwise coprime when GCD(A_i,A_j)=1 holds for every pair (i, j) such that 1\leq i < j \leq N.

\{A_i\} is said to be setwise coprime when \{A_i\} is not pairwise coprime but GCD(A_1,\ldots,A_N)=1.

Determine if \{A_i\} is pairwise coprime, setwise coprime, or neither.

Here, GCD(\ldots) denotes greatest common divisor.

Constraints

2 \leq N \leq 10^6

1 \leq A_i\leq 10^6

Input

Input is given from Standard Input in the following format:

N
A_1 \ldots A_N

Output

If \{A_i\} is pairwise coprime, print pairwise coprime; if \{A_i\} is setwise coprime, print setwise coprime; if neither, print not coprime.

Sample Input 1

3
3 4 5

Sample Output 1

pairwise coprime

GCD(3,4)=GCD(3,5)=GCD(4,5)=1, so they are pairwise coprime.

Sample Input 2

3
6 10 15

Sample Output 2

setwise coprime

Since GCD(6,10)=2, they are not pairwise coprime. However, since GCD(6,10,15)=1, they are setwise coprime.

Sample Input 3

3
6 10 16

Sample Output 3

not coprime

GCD(6,10,16)=2, so they are neither pairwise coprime nor setwise coprime.

Solution in Haskell (GHC 8.8.3) (judge verdict: Accepted)

{-# LANGUAGE BangPatterns               #-}
{-# LANGUAGE BinaryLiterals             #-}
{-# LANGUAGE CPP                        #-}
{-# LANGUAGE DeriveFunctor              #-}
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE DerivingVia                #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures             #-}
{-# LANGUAGE LambdaCase                 #-}
{-# LANGUAGE MagicHash                  #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE MultiWayIf                 #-}
{-# LANGUAGE NumericUnderscores         #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE PatternSynonyms            #-}
{-# LANGUAGE RankNTypes                 #-}
{-# LANGUAGE RecordWildCards            #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE StandaloneDeriving         #-}
{-# LANGUAGE TupleSections              #-}
{-# LANGUAGE TypeApplications           #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE TypeInType                 #-}
{-# LANGUAGE UnboxedTuples              #-}
{- base -}
import           Control.Applicative
import qualified Control.Arrow                     as Arrow
import           Control.Monad
import           Control.Monad.ST
import           Data.Bits
import           Data.Bool
import qualified Data.Char                         as Char
import           Data.Complex
import qualified Data.Foldable                     as Foldable
import           Data.Function
import qualified Data.List                         as List
import           Data.Maybe
import           Data.Monoid
import           Data.Ord
import           Data.Ratio
import           Data.Semigroup
import qualified Data.Word                         as Word
import           Foreign                           hiding (void)
import           GHC.Exts
import           Unsafe.Coerce
{- array -}
import qualified Data.Array.IO                     as ArrIO
import qualified Data.Array.MArray                 as ArrMA
import qualified Data.Array.ST                     as ArrST
import qualified Data.Array.Storable               as ArrStore
import qualified Data.Array.Unboxed                as ArrU
{- bytestring -}
import qualified Data.ByteString                   as BS
import qualified Data.ByteString.Builder           as BSB
import qualified Data.ByteString.Builder.Extra     as BSBE
import qualified Data.ByteString.Char8             as BSC8
import qualified Data.ByteString.Lazy              as BSL
import qualified Data.ByteString.Lazy.Builder      as BSLB
import qualified Data.ByteString.Lazy.Char8        as BSLC8
import qualified Data.ByteString.Unsafe            as BSU
{- containers -}
import qualified Data.Graph                        as Graph
import           Data.IntMap                       (IntMap)
import qualified Data.IntMap                       as IntMap
import           Data.IntSet                       (IntSet)
import qualified Data.IntSet                       as IntSet
import qualified Data.Sequence                     as Seq
import qualified Data.Tree                         as Tree
{- integer-gmp -}
import           GHC.Integer.GMP.Internals
{- time -}
import qualified Data.Time.Calendar                as Calender
import qualified Data.Time.Calendar.Easter         as CalenderE
import qualified Data.Time.Calendar.Julian         as CalenderJ
import qualified Data.Time.Calendar.MonthDay       as CalenderM
import qualified Data.Time.Calendar.OrdinalDate    as CalenderD
import qualified Data.Time.Calendar.WeekDate       as CalenderW
import qualified Data.Time.LocalTime               as LocalTime
{- transformers -}
import qualified Control.Monad.Trans.Accum         as TAccum
import qualified Control.Monad.Trans.Cont          as TCont
import qualified Control.Monad.Trans.Identity      as TId
import qualified Control.Monad.Trans.Reader        as TReader
import qualified Control.Monad.Trans.State.Lazy    as TStateL
import qualified Control.Monad.Trans.State.Strict  as TStateS
import qualified Control.Monad.Trans.Writer.CPS    as TWriteC
import qualified Control.Monad.Trans.Writer.Lazy   as TWriteL
import qualified Control.Monad.Trans.Writer.Strict as TWriteS
{- vector -}
import qualified Data.Vector                       as V
import qualified Data.Vector.Generic               as VG
import qualified Data.Vector.Generic.Mutable       as VGM
import qualified Data.Vector.Mutable               as VM
import qualified Data.Vector.Primitive             as VP
import qualified Data.Vector.Primitive.Mutable     as VPM
import qualified Data.Vector.Unboxed               as VU
import qualified Data.Vector.Unboxed.Mutable       as VUM
-------------------------------------------------------------------------------
main :: IO ()
main = do
  n <- parse1
  a <- parseM n
  let p1 = snd $ VU.foldr func1 (IntSet.empty, True) a
  let p2 = (1 == VU.foldr1 gcd a)
  putStrLn $ if p1
              then "pairwise coprime"
              else if p2
                then "setwise coprime"
                else "not coprime"

func1 :: Int -> (IntSet, Bool) -> (IntSet, Bool)
func1 x (se, b)
  | not b = (se, b)
  | IntSet.disjoint a se = (IntSet.union se a, b)
  | otherwise = (se, False)
  where
    a = primeFactorsSet x
-------------------------------------------------------------------------------
pSpin :: Num int => int -> [int] -> [int]
pSpin x (y:ys) = x : pSpin (x + y) ys

type PWheel int      = ([int], [int])
data PQueue int
  = Empty
  | Fork [int] [PQueue int]
type PComposites int = (PQueue int, [[int]])

pEnqueue :: Ord int => [int] -> PQueue int -> PQueue int
pEnqueue ns = pMerge (Fork ns [])

pMergeAll :: Ord int => [PQueue int] -> PQueue int
pMergeAll []       = Empty
pMergeAll [x]      = x
pMergeAll (x:y:qs) = pMerge (pMerge x y) (pMergeAll qs)

pDequeue :: Ord int => PQueue int -> ([int], PQueue int)
pDequeue (Fork ns qs) = (ns, pMergeAll qs)

pMerge :: Ord int => PQueue int -> PQueue int -> PQueue int
pMerge Empty y    = y
pMerge x Empty    = x
pMerge x y
  | prio x <= prio y = join x y
  | otherwise        = join y x
  where
    prio (Fork (n:_) _) = n
    join (Fork ns qs) q = Fork ns (q:qs)

pDiscard :: Ord int => int -> PComposites int -> PComposites int
pDiscard n ns
  | n == m    = pDiscard n ms
  | otherwise = ns
  where
    (m, ms) = pSplitComposites ns

pSplitComposites :: Ord int => PComposites int -> (int, PComposites int)
pSplitComposites (Empty, xs:xss) = pSplitComposites (Fork xs [], xss)
pSplitComposites (queue, xss@((x:xs):yss))
  | x < z     = (x, pDiscard x (pEnqueue xs queue, yss))
  | otherwise = (z, pDiscard z (pEnqueue zs queue', xss))
  where
    (z:zs, queue') = pDequeue queue

pSieveComps :: (Ord int, Num int) => int -> [int] -> PComposites int -> [[int]]
pSieveComps cand ns@(m:ms) xs
  | cand == comp = pSieveComps (cand+m) ms ys
  | cand <  comp = pSpin cand ns : pSieveComps (cand + m) ms xs
  | otherwise    = pSieveComps cand ns ys
  where
    (comp, ys) = pSplitComposites xs

pComposites :: (Ord int, Num int) => int -> [int] -> PComposites int
pComposites p ns = (Empty, map comps (pSpin p ns: pSieve p ns))
  where
    comps xs@(x:_) = map (x*) xs

pSieve :: (Ord int, Num int) => int -> [int] -> [[int]]
pSieve p ns@(m:ms) = pSpin p ns : pSieveComps (p+m) ms (pComposites p ns)

pCancel :: Integral int => int -> int -> int -> [int] -> [int]
pCancel 0 _ _ _ = []
pCancel m p n (x:ys@(y:zs))
  | nx `mod` p > 0 = x : pCancel (m - x) p nx ys
  | otherwise      = pCancel m p n (x+y:zs)
  where
    nx = n + x

pNext :: Integral int => PWheel int -> PWheel int
pNext (ps@(p:_), xs) = (py:ps, pCancel (product ps) p py ys)
  where
    (y:ys) = cycle xs
    py = p + y

pWheel :: Integral int => Int -> PWheel int
pWheel n = iterate pNext ([2], [1]) !! n

pWheelSieve :: Integral int => Int -> [int]
pWheelSieve k = reverse ps ++ map head (pSieve p (cycle ns))
  where
    (p:ps,ns) = pWheel k

{- public -}
primeFactors :: Integral int => int -> [int]
primeFactors n = factors n (pWheelSieve 6)
  where
    factors 1 _      = []
    factors m (p:ps)
      | m < p * p = [m]
      | r == 0    = p : factors q (p:ps)
      | otherwise = factors m ps
      where
        (q, r) = quotRem m p

{- private -}
primeFactorsSet :: Int -> IntSet
primeFactorsSet n = factors n (pWheelSieve 6)
  where
    factors :: Int -> [Int] -> IntSet
    factors 1 _ = IntSet.empty
    factors m (p:ps)
      | m < p * p = IntSet.singleton m
      | r == 0    = IntSet.insert p $ factors q (p:ps)
      | otherwise = factors m ps
      where (q, r) = quotRem m p

{- public -}
primes :: Integral int => [int]
primes = pWheelSieve 6

{- public -}
isPrime :: Integral int => int -> Bool
isPrime n
  | n > 1 = primeFactors n == [n]
  | otherwise = False

{- public -}
composites :: Integral int => [int]
composites = List.unfoldr (\(a1: as@(a2: at), ps@(ph: pt)) -> Just (a1, if a2 == ph then (at, pt) else (as, ps))) ([4..], drop 2 primes)

{- public -}
nextPrimeInt :: Int -> Int
nextPrimeInt = fromInteger . nextPrimeInteger . fromIntegral

sieveUA :: Int -> ArrU.UArray Int Bool
sieveUA top = ArrST.runSTUArray $ do
  let m = (top-1) `div` 2
      r = floor . sqrt $ fromIntegral top + 1
  sieve <- ArrST.newArray (1,m) True
  forM_ [1..r `div` 2] $ \i -> do
    isPrime <- ArrST.readArray sieve i
    when isPrime $ do
      forM_ [2*i*(i+1), 2*i*(i+2)+1..m] $ \j -> do
        ArrST.writeArray sieve j False
  return sieve

{- public -}
primesToUA :: Int -> [Int]
primesToUA top = 2 : [i*2+1 | (i,True) <- ArrU.assocs $ sieveUA top]

{- public -}
prevPrimeInt :: Int -> Int
prevPrimeInt n
  | n <= 2    = -1
  | otherwise = f n
  where
    f = last2 . primesToUA
    last2 :: [a] -> a
    last2 []     = error "lack of list elements"
    last2 [x]    = error "lack of list elements"
    last2 [x,y]  = x
    last2 (x:xs) = last2 xs

{- public -}
millerRabin :: Int -> Bool
millerRabin n
  |  n <= 1 = False
  |  n == 2
  || n == 3
  || n == 5
  || n == 7 = True
  |  even n = False
  |  otherwise = mrCheck $ fromIntegral n
powerMR :: Integer -> Integer -> Integer -> Integer
powerMR b e m = loop 1 (b `mod` m) e
  where
    loop res base pxe
      | pxe <= 0 = res
      | otherwise =
        let res'  = if pxe `mod` 2 == 1 then (res * base) `mod` m else res
            pxe'  = shift pxe (-1)
            base' = (base * base) `mod` m
        in loop res' base' pxe'
factoringPowers :: Integer -> (Integer, Integer)
factoringPowers n = loop (n - 1) 0
  where
    loop d s
      | even d    = loop (d `div` 2) (s + 1)
      | otherwise = (s, d)
mrCheck :: Integer -> Bool
mrCheck p
  | p < 2047                = loop [2]
  | p < 9080191             = loop [31,73]
  | p < 4759123141          = loop [2,7,61]
  | p < 1122004669633       = loop [2,13,23,1662803]
  | p < 2152302898747       = loop [2,3,5,7,11]
  | p < 341550071728321     = loop [2,3,5,7,11,13,17]
  | p < 3825123056546413051 = loop [2,3,5,7,11,13,17,19,23]
  | p < 9223372036854775808 = loop [2,325,9375,28178,450775,9780504,1795265022]
  | otherwise               = loop [ 2 .. min (p - 1) (floor $ 2 * (log p')^(2 :: Int)) ]
  where
    p' = fromIntegral p :: Double
    (s, d) = factoringPowers p
    loop [] = True
    loop (a:as)
      | (powerMR a d p) /= 1 && powLoop 0 = False
      | otherwise = loop as
      where
        powLoop r
          | r < s     = (powerMR a (2 ^ r * d) p) /= (p - 1)  && powLoop (r + 1)
          | otherwise = True

{- public -}
totient :: Int -> Int
totient n = n `quot` List.product ps * (List.product $ map (subtract 1) ps)
  where ps = map head . List.group $ primeFactors n

{- public -}
divisor :: Int -> [Int]
divisor n = List.foldr f [] $ List.takeWhile ((<= n) . (^ 2)) [1 .. n]
  where
    f x ds
      | r == 0, q /= x = x : q : ds
      | r == 0         = x : ds
      | otherwise      = ds
      where
        (q, r) = n `divMod` x
-------------------------------------------------------------------------------
type Parser a = BSC8.ByteString -> Maybe (a, BSC8.ByteString)
parseInt :: Parser Int
parseInt = fmap (Arrow.second BSC8.tail) . BSC8.readInt
parseChar :: [Char] -> VU.Vector Char
parseChar = VU.fromList
parse1 :: IO Int
parse1 = readLn
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
parse4 :: IO (Int, Int, Int, Int)
parse4 = (\vec -> (vec VU.! 0, vec VU.! 1, vec VU.! 2, vec VU.! 3)) . VU.unfoldrN 4 parseInt <$> BSC8.getLine
parseM :: Int -> IO (VU.Vector Int)
parseM m = VU.unfoldrN m parseInt <$> BSC8.getLine
parseN :: Int -> IO (VU.Vector Int)
parseN n = VU.replicateM n parse1
parseNM :: Int -> Int -> IO (V.Vector (VU.Vector Int))
parseNM n m = V.replicateM n $ VU.unfoldrN m parseInt <$> BSC8.getLine
parseANBN :: Int -> IO (VU.Vector Int, VU.Vector Int)
parseANBN n = do
  vectup <- VU.replicateM n $ (\vec -> (vec VU.! 0, vec VU.! 1)) . VU.unfoldr (BSC8.readInt . BSC8.dropWhile Char.isSpace) <$> BSC8.getLine
  return $ VU.unzip vectup
parseANBNCN :: Int -> IO (VU.Vector Int, VU.Vector Int, VU.Vector Int)
parseANBNCN n = do
  vectup <- VU.replicateM n $ (\vec -> (vec VU.! 0, vec VU.! 1, vec VU.! 2)) . VU.unfoldr (BSC8.readInt . BSC8.dropWhile Char.isSpace) <$> BSC8.getLine
  return $ VU.unzip3 vectup