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 Rust (1.42.0) (judge verdict: Accepted)

#![allow(unused_imports)]

// language: Rust(1.42.0)
// check available crates on AtCoder at "https://atcoder.jp/contests/language-test-202001"
// My Library Repositry is at "https://github.com/Loptall/sfcpl"

use itertools::*;
use itertools_num::*;
use lazy_static::lazy_static;
use maplit::{btreemap, btreeset, hashmap, hashset};
use num_bigint::{BigInt, BigUint};
use num_complex::Complex;
use num_integer::{binomial, gcd, lcm, multinomial, Integer};
use num_rational::Rational;
use num_traits::{
    clamp, one, pow, zero, Num, NumAssignOps, NumOps, One, Pow, Signed, Unsigned, Zero,
};
use permutohedron::Heap;
use proconio::{
    derive_readable, fastout, input, is_stdin_empty,
    marker::{Bytes, Chars, Isize1, Usize1},
};
use rand::random;
use std::cmp::{
    max, min, Ordering,
    Ordering::{Equal, Greater, Less},
    Reverse,
};
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
use std::convert::TryInto;
use std::fmt;
use std::mem::swap;
use std::num::{NonZeroU32, ParseIntError};
use std::ops::{
    Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
    Index, IndexMut, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub,
    SubAssign,
};
use std::process::exit;
use std::{f32, f64, i128, i16, i32, i64, i8, isize, u128, u16, u32, u64, u8, usize};

pub use visualize::*;
pub mod visualize {
    use itertools::*;
    pub trait Visualize {
        fn visualize(&self, split: &str);
        fn continuous(&self) {
            self.visualize("");
        }
        fn spaces(&self) {
            self.visualize(" ");
        }
        fn lines(&self) {
            self.visualize("\n");
        }
    }
    macro_rules ! impl_vis_for_sized {($ ($ t : ty ) ,+ ) => {$ (impl Visualize for $ t {fn visualize (& self , _split : & str ) {print ! ("{}" , self ) ; } } ) + } ; }
    impl_vis_for_sized! {usize , u8 , u16 , u32 , u64 , u128 , isize , i8 , i16 , i32 , i64 , i128 , String , & str , char }
    impl<T: std::fmt::Display> Visualize for [T] {
        fn visualize(&self, split: &str) {
            print!("{}", self.iter().join(split));
        }
    }
    #[macro_export]
    macro_rules ! vis {() => {println ! () ; } ; ($ last : expr ; ) => {$ last . lines () ; vis ! () } ; ($ last : expr => ) => {$ last . continuous () ; vis ! () ; } ; ($ last : expr $ (, ) ? ) => {$ last . spaces () ; vis ! () ; } ; ($ first : expr ; $ ($ rest : tt ) * ) => {$ first . lines () ; println ! () ; vis ! ($ ($ rest ) * ) ; } ; ($ first : expr => $ ($ rest : tt ) * ) => {$ first . continuous () ; vis ! ($ ($ rest ) * ) ; } ; ($ first : expr , $ ($ rest : tt ) * ) => {$ first . spaces () ; print ! (" " ) ; vis ! ($ ($ rest ) * ) ; } ; }
}

pub use consts::*;
pub mod consts {
    pub const MOD10E9_7: usize = 1000000007; // 10 ^ 9 + 7
    pub const MOD99_: usize = 998244353;
    pub const MAX: usize = std::usize::MAX; // = 2 ^ 64 - 1 = 18446744073709551615 ≈ 1.8 * 10 ^ 19
    pub const INF: usize = 2000000000000000000; // MAX / 9 < 2 * 10e18 < MAX / 10
    pub const FNI: i64 = -2000000000000000000; // == -(INF as i64)
    pub const PI: f64 = std::f64::consts::PI; // 3.141592653589793 -- 10 ^ -15
    pub const ASCII_A_LARGE: u8 = 65;
    pub const ASCII_A_SMALL: u8 = 97;
    pub const ASCII_0: u8 = 48;
    pub const ADJ4: &[(isize, isize); 4] = &[(1, 0), (0, 1), (-1, 0), (0, -1)];
    pub const ADJ8: &[(isize, isize); 8] = &[
        (1, 0),
        (1, 1),
        (0, 1),
        (-1, 1),
        (-1, 0),
        (-1, -1),
        (0, -1),
        (1, -1),
    ];
}

// snippet here

pub use sieve::*;
pub mod sieve {
    use std::collections::BTreeMap;
    /// 初期化の際にだけエラストテネスの篩を使って素数のリストを生成
    ///
    /// `O(n)`
    pub struct Sieve {
        size: usize,
        /// sieve[i] => i が振り落とされた数
        spf: Vec<usize>,
        /// `(0..=n)`までの範囲で
        /// `is_prime[i]` => `i`は素数
        is_prime: Vec<bool>,
        /// `(0..=n)`の全ての素数
        primes: Vec<usize>,
    }

    impl Sieve {
        /// 初期化、サイズが重要
        pub fn new(n: usize) -> Self {
            let mut spf: Vec<usize> = (0..=n).collect();
            let mut is_prime = vec![true; n + 1];

            is_prime[0] = false;
            is_prime[1] = false;

            for i in (2..).take_while(|&x| x * x <= n) {
                if spf[i] == i {
                    for j in (i * i..=n).step_by(i) {
                        if spf[j] == j {
                            is_prime[j] = false;
                            spf[j] = i;
                        }
                    }
                }
            }

            let primes = is_prime
                .iter()
                .enumerate()
                .filter(|x| *x.1)
                .map(|x| x.0)
                .collect();

            Self {
                size: n,
                spf,
                is_prime,
                primes,
            }
        }

        /// 自分自身の素数リストの有効な範囲
        pub fn size(&self) -> usize {
            self.size
        }

        /// `n`以下の全ての素数のリストを作る
        pub fn primes(&self, n: usize) -> Vec<usize> {
            assert!(self.size() >= n);
            self.primes
                .iter()
                .take_while(|x| **x <= n)
                .cloned()
                .collect()
        }

        /// `n`が素数かどうか
        ///
        /// # Panic
        /// `n > self.size`でindexing panicする
        pub fn is_prime(&self, n: usize) -> bool {
            self.is_prime[n]
        }
    }

    /// Seiveテーブルを用いた素因数分解
    ///
    /// `n`を素因数分解するためには最小で、
    /// `√n`までのサイズの素数テーブルが必要
    ///
    /// # Panic
    /// `sieve`のサイズが`√n`未満で不十分な場合にpanicします
    pub fn factorizations_with_sieve(sieve: &Sieve, n: usize) -> BTreeMap<usize, usize> {
        // Panic
        assert!(sieve.size.pow(2) >= n);

        let mut res = BTreeMap::new();

        factorizations_with_sieve_inner(sieve, n, &mut res);

        res
    }

    fn factorizations_with_sieve_inner(sieve: &Sieve, n: usize, res: &mut BTreeMap<usize, usize>) {
        if n == 1 {
            return;
        }

        let d = sieve.spf[n];
        *res.entry(d).or_insert(0) += 1;
        factorizations_with_sieve_inner(sieve, n / d, res);
    }

    #[test]
    fn sieve_test() {
        let sieve = Sieve::new(10);
        assert_eq!(vec![2, 3, 5, 7], sieve.primes(10));
        assert_eq!(sieve.is_prime[2], true);
        assert_eq!(sieve.is_prime[9], false);
    }

    #[test]
    fn fact_test() {
        use maplit::btreemap;
        let sieve = Sieve::new(10000007);
        let f = factorizations_with_sieve(&sieve, 12);
        assert_eq!(f, btreemap![2 => 2, 3 => 1]);
        let f = factorizations_with_sieve(&sieve, 107);
        assert_eq!(f, btreemap![107 => 1]);
        // let f = factorizations_with_sieve(&sieve, 1000000007);
        // assert_eq!(f, btreemap![1000000007 => 1]);
    }
}

// #[fastout]
fn main() {
    input! {
        n: usize,
        a: [usize; n]
    }

    let sieve = Sieve::new(10usize.pow(6));

    let mut count = vec![0usize; sieve.size()];

    for &i in a.iter() {
        let f = factorizations_with_sieve(&sieve, i);
        for &p in f.keys() {
            count[p] += 1;
        }
    }

    if count.iter().all(|x| *x <= 1) {
        vis!("pairwise coprime");
        return;
    }

    let mut g = a[0];
    for i in 1..n {
        g = gcd(g, a[i]);
    }

    if g == 1 {
        vis!("setwise coprime");
    } else {
        vis!("not coprime");
    }

}