// https://yukicoder.me/problems/no/3030 #include #include #include #include #include #include #include #include #include using i64 = int_fast64_t; using u64 = uint_fast64_t; using i32 = int_fast32_t; using u32 = uint_fast32_t; namespace mlib { struct constexpr_string { constexpr constexpr_string(const char *str): str(str) {} const char *str; constexpr bool strcmp_begin(const constexpr_string st) const { return st.str[0] == '\x00' || (this->str[0] != '\x00' && this->str[0] == st.str[0] && constexpr_string(this->str + 1).strcmp_begin(st.str + 1)); } }; template constexpr bool is_type_in_root_namespace(const constexpr_string name) { return constexpr_string(__PRETTY_FUNCTION__ + (sizeof("constexpr bool mlib::is_type_in_root_namespace(mlib::constexpr_string) [with T = ") - 1)).strcmp_begin(name); } } namespace mlib { constexpr double pi = std::acos(-1); using std::pow; template("mlib"), std::nullptr_t> = nullptr> T pow(T x, U n) { T res = 1; T a = x; while(n != 0) { if(n % 2 == 1) res *= a; a *= a; n /= 2; } return res; } template T mod_pow(const T &x, const T &n, const T &mod) { T res = 1; T a = x; T ll = n; while(ll != 0) { if(ll % 2 == 1) res = mod_mul(res, a, mod); a = mod_mul(a, a, mod); ll /= 2; } return res; } // @mod needs prime. template T mod_inv(T x, T mod) { assert(mod > 0); if(mod == 1) return 0; return mod_pow(x, mod - 2, mod); } template T mod_add(T lhs, T rhs, T mod) { assert(mod > 0); lhs %= mod, rhs %= mod; lhs += rhs; if(lhs >= mod) lhs -= mod; return lhs; } template T mod_sub(T lhs, T rhs, T mod) { assert(mod > 0); lhs %= mod, rhs %= mod; if(lhs < rhs) lhs += mod; lhs -= rhs; return lhs; } template T mod_mul(T lhs, T rhs, T mod) { assert(mod > 0); return ((lhs % mod) * (rhs % mod)) % mod; } // @mod needs prime. template T mod_div(T lhs, T rhs, T mod) { assert(mod > 0); assert(rhs > 0); lhs %= mod, rhs %= mod; return mod_mul(lhs, mod_inv(rhs, mod), mod); } } namespace mlib { class FFT { public: static std::vector> fft(std::vector> &f); static std::vector> ifft(std::vector> &f); }; } using namespace mlib; std::vector> FFT::fft(std::vector> &f) { u32 n = f.size(); if(n <= 1) return f; std::vector> res = f; // Bit rev u32 s = 0; while((u32)(1 << s) < n) s++; for(u32 i = 0; i < n; i++) { u32 j = 0; for(u32 k = 0; k < s; k++) j |= (i >> k & 1) << (s - 1 - k); if(i < j) std::swap(res[i], res[j]); } double ang = -1.0 * pi / (2.0 * n); std::complex zeta1, zeta3, x0, x1, x3; u32 m, mm, rev, k1, k2, k3; for(m = 4; m <= n; m <<= 1) { mm = m >> 2; for(u32 j = mm; j >= 1; j >>= 2) { for(u32 k = mm - j; k < mm - (j >> 1); k++) { k1 = k + mm; k2 = k1 + mm; k3 = k2 + mm; x1 = res[k] - res[k1]; res[k] += res[k1]; x3 = res[k3] - res[k2]; res[k2] += res[k3]; res[k1] = std::complex(x1.real() - x3.imag(), x1.imag() + x3.real()); //res[k1] = x1 + x3 * ui; res[k3] = std::complex(x1.real() + x3.imag(), x1.imag() - x3.real()); // res[k3] = x1 - x3 * ui; } } if(m == n) continue; rev = n >> 1; zeta1 = std::cos(ang * rev); for(u32 j = mm; j >= 1; j >>= 2) { for(u32 k = m + mm - j; k < m + mm - (j >> 1); k++) { k1 = k + mm; k2 = k1 + mm; k3 = k2 + mm; x1 = res[k] - res[k1]; res[k] += res[k1]; x3 = res[k3] - res[k2]; res[k2] += res[k3]; x0 = std::complex(x1.real() - x3.imag(), x1.imag() + x3.real()); // x0 = x1 + x3 * ui; x0 = std::complex(x0.real() + x0.imag(), x0.imag() - x0.real()); // x0 *= 1.0 - ui; res[k1] = zeta1 * x0; x0 = std::complex(x1.real() + x3.imag(), x1.imag() - x3.real()); // x0 = x1 - x3 * ui; x0 = std::complex(-x0.real() + x0.imag(), -x0.imag() - x0.real()); // x0 *= -1.0 - ui; res[k3] = zeta1 * x0; } } for(u32 i = 2 * m; i < n; i += m) { for(u32 j = n >> 1; j > (rev ^= j); j >>= 1); zeta1 = std::polar(1.0, ang * rev); zeta3 = std::polar(1.0, 3.0 * ang * rev); for(u32 j = mm; j >= 1; j >>= 2) { for(u32 k = i + mm - j; k < i + mm - (j >> 1); k++) { k1 = k + mm; k2 = k1 + mm; k3 = k2 + mm; x1 = res[k] - res[k1]; res[k] += res[k1]; x3 = res[k3] - res[k2]; res[k2] += res[k3]; x0 = std::complex(x1.real() - x3.imag(), x1.imag() + x3.real()); // x0 = x1 + x3 * ui; res[k1] = zeta1 * x0; x0 = std::complex(x1.real() + x3.imag(), x1.imag() - x3.real()); // x0 = x1 - x3 * ui; res[k3] = zeta3 * x0; } } } } mm = n >> 1; for(u32 j = mm; j >= 1; j >>= 2) { for(u32 k = mm - j; k < mm - (j >> 1); k++) { k1 = mm + k; x0 = res[k] - res[k1]; res[k] += res[k1]; res[k1] = x0; } } return res; } std::vector> FFT::ifft(std::vector> &f) { u32 n = f.size(); if(n <= 1) return f; std::vector> res = f; // Bit rev u32 s = 0; while((u32)(1 << s) < n) s++; for(u32 i = 0; i < n; i++) { u32 j = 0; for(u32 k = 0; k < s; k++) j |= (i >> k & 1) << (s - 1 - k); if(i < j) std::swap(res[i], res[j]); } double ang = 1.0 * pi / (2.0 * n); std::complex zeta1, zeta3, x0, x1, x3; u32 m, mm, rev, k1, k2, k3; for(m = 4; m <= n; m <<= 1) { mm = m >> 2; for(u32 j = mm; j >= 1; j >>= 2) { for(u32 k = mm - j; k < mm - (j >> 1); k++) { k1 = k + mm; k2 = k1 + mm; k3 = k2 + mm; x1 = res[k] - res[k1]; res[k] += res[k1]; x3 = res[k3] - res[k2]; res[k2] += res[k3]; res[k1] = std::complex(x1.real() + x3.imag(), x1.imag() - x3.real()); // res[k1] = x1 - x3 * ui; res[k3] = std::complex(x1.real() - x3.imag(), x1.imag() + x3.real()); // res[k3] = x1 + x3 * ui; } } if(m == n) continue; rev = n >> 1; zeta1 = std::cos(ang * rev); for(u32 j = mm; j >= 1; j >>= 2) { for(u32 k = m + mm - j; k < m + mm - (j >> 1); k++) { k1 = k + mm; k2 = k1 + mm; k3 = k2 + mm; x1 = res[k] - res[k1]; res[k] += res[k1]; x3 = res[k3] - res[k2]; res[k2] += res[k3]; x0 = std::complex(x1.real() + x3.imag(), x1.imag() - x3.real()); // x0 = x1 - x3 * ui; x0 = std::complex(x0.real() - x0.imag(), x0.imag() + x0.real()); // x0 *= 1.0 + ui; res[k1] = zeta1 * x0; x0 = std::complex(x1.real() - x3.imag(), x1.imag() + x3.real()); // x0 = x1 + x3 * ui; x0 = std::complex(-x0.real() - x0.imag(), x0.real() + x0.imag()); // x0 *= -1.0 + ui; res[k3] = zeta1 * x0; } } for(u32 i = 2 * m; i < n; i += m) { for(u32 j = n >> 1; j > (rev ^= j); j >>= 1); zeta1 = std::polar(1.0, ang * rev); zeta3 = std::polar(1.0, 3.0 * ang * rev); for(u32 j = mm; j >= 1; j >>= 2) { for(u32 k = i + mm - j; k < i + mm - (j >> 1); k++) { k1 = k + mm; k2 = k1 + mm; k3 = k2 + mm; x1 = res[k] - res[k1]; res[k] += res[k1]; x3 = res[k3] - res[k2]; res[k2] += res[k3]; x0 = std::complex(x1.real() + x3.imag(), x1.imag() - x3.real()); // x0 = x1 - x3 * ui; res[k1] = zeta1 * x0; x0 = std::complex(x1.real() - x3.imag(), x1.imag() + x3.real()); // x0 = x1 + x3 * ui; res[k3] = zeta3 * x0; } } } } mm = n >> 1; for(u32 j = mm; j >= 1; j >>= 2) { for(u32 k = mm - j; k < mm - (j >> 1); k++) { k1 = k + mm; x0 = res[k] - res[k1]; res[k] += res[k1]; res[k1] = x0; } } return res; } namespace mlib { // number of base digits constexpr u64 MAX_DIGITNUM = 5; // radix base constexpr u64 RADIX_BASE = 10; // radix constexpr u64 RADIX = std::pow(RADIX_BASE, MAX_DIGITNUM); // sign using SIGN = bool; constexpr bool PLUS = true; constexpr bool MINUS = false; using DIGITS = std::vector; class Int { private: // 12345678 => digits = {78, 56, 34, 12} DIGITS digits; SIGN sign; void assignment(const Int &x) noexcept; void to_zero() noexcept; void to_radix_power(u64 n) noexcept; void push_up(u64 num) noexcept; // link as upper digit // unsigned operations // absolute peer arithmetics // The argument does not have to be non-negative. void add(const Int &x) noexcept; SIGN sub(const Int &x) noexcept; // automatically subtracted from the larger one void mul(const Int &x) noexcept; void div_short(const Int &x, bool rem = false) noexcept; void div(const Int &x, bool rem = false) noexcept; bool is_equal(const Int &x) const noexcept; bool less_than(const Int &x, bool equal = false) const noexcept; bool greater_than(const Int &x, bool equal = false) const noexcept; // absolute Int abs() const noexcept; // signed operations (arithmetic) void a_add(const Int &x) noexcept; void a_sub(const Int &x) noexcept; void a_mul(const Int &x) noexcept; void a_div(const Int &x) noexcept; void a_mod(const Int &x) noexcept; bool a_is_equal(const Int &x) const noexcept; bool a_less_than(const Int &x, bool equal = false) const noexcept; bool a_greater_than(const Int &x, bool equal = false) const noexcept; public: // default constructor Int(); // integer constructor template::value, std::nullptr_t> = nullptr> Int(T n): Int(std::to_string(n)) { } Int(const DIGITS &digits); // std::string constructor Int(const std::string &x); size_t size() const noexcept; u64 at(i64 n) const noexcept; u64 most_sig() const noexcept; void resize(size_t size) noexcept; void normalize() noexcept; // remove meaningless 0 from the top digit void align() noexcept; // adjusted so that each digit does not exceed RADIX /* operators */ // io friend std::ostream &operator <<(std::ostream &os, Int &x) noexcept { if(x.sign == MINUS && x != 0) os << "-"; os << x.digits.at(x.size() - 1); for(size_t i = x.size() - 1; i --> 0;) os << std::setw(MAX_DIGITNUM) << std::setfill('0') << x.digits.at(i); return os; } friend std::ostream &operator <<(std::ostream &os, Int &&x) noexcept { if(x.sign == MINUS && x != 0) os << "-"; os << x.digits.at(x.size() - 1); for(size_t i = x.size() - 1; i --> 0;) os << std::setw(MAX_DIGITNUM) << std::setfill('0') << x.digits.at(i); return os; } friend std::istream &operator >>(std::istream &is, Int &x) noexcept { std::string s; is >> s; x = Int(s); return is; } // arithmetic Int &operator +=(const Int &x) noexcept { a_add(x); return *this; } Int &operator -=(const Int &x) noexcept { a_sub(x); return *this; } Int &operator *=(const Int &x) noexcept { a_mul(x); return *this; } Int &operator /=(const Int &x) noexcept { a_div(x); return *this; } Int &operator %=(const Int &x) noexcept { a_mod(x); return *this; } friend Int operator +(Int lhs, const Int &rhs) noexcept { lhs += rhs; return lhs; } friend Int operator -(Int lhs, const Int &rhs) noexcept { lhs -= rhs; return lhs; } friend Int operator *(Int lhs, const Int &rhs) noexcept { lhs *= rhs; return lhs; } friend Int operator /(Int lhs, const Int &rhs) noexcept { lhs /= rhs; return lhs; } friend Int operator %(Int lhs, const Int &rhs) noexcept { lhs %= rhs; return lhs; } // unary Int &operator ++() noexcept { operator +=(1); return *this; } Int operator ++(int) noexcept { Int x = *this; operator +=(1); return x; } Int &operator --() noexcept { operator -=(1); return *this; } Int operator --(int) noexcept { Int x = *this; operator -=(1); return x; } Int operator +() const noexcept { return *this; } Int operator -() const noexcept { Int x = *this; x.sign = !x.sign; return x; } // comparison friend bool operator ==(const Int &lhs, const Int &rhs) noexcept { return lhs.a_is_equal(rhs); } friend bool operator !=(const Int &lhs, const Int &rhs) noexcept { return !lhs.a_is_equal(rhs); } friend bool operator <(const Int &lhs, const Int &rhs) noexcept { return lhs.a_less_than(rhs); } friend bool operator <=(const Int &lhs, const Int &rhs) noexcept { return lhs.a_less_than(rhs, true); } friend bool operator >(const Int &lhs, const Int &rhs) noexcept { return lhs.a_greater_than(rhs); } friend bool operator >=(const Int &lhs, const Int &rhs) noexcept { return lhs.a_greater_than(rhs, true); } }; inline Int Int::abs() const noexcept { if(this->sign == PLUS) return *this; Int res = *this; res.sign = PLUS; return res; } inline void Int::resize(size_t size) noexcept { this->digits.resize(size, 0); } inline void Int::normalize() noexcept { while(size() > 0) { if(this->digits.back() == 0) this->digits.pop_back(); else break; } if(size() == 0) to_zero(); } inline u64 Int::at(i64 n) const noexcept { return this->digits.at(n); } inline void Int::assignment(const Int &x) noexcept { this->digits = x.digits; this->sign = x.sign; } inline void Int::to_zero() noexcept { std::vector().swap(this->digits); push_up(0); } inline void Int::to_radix_power(u64 n) noexcept { if(n == 0) { assignment(1); return; } to_zero(); this->digits.reserve(n); for(u64 i = 0; i < n -1; i++) push_up(0); push_up(1); } inline void Int::push_up(u64 num) noexcept { this->digits.emplace_back(num); } inline size_t Int::size() const noexcept { return this->digits.size(); } inline u64 Int::most_sig() const noexcept { return this->digits.back(); } inline void Int::align() noexcept { for(size_t i = 0; i < this->digits.size() - 1; i++) { if(this->digits.at(i) >= RADIX) { this->digits.at(i + 1) += this->digits.at(i) / RADIX; this->digits.at(i) %= RADIX; } } if(this->digits.back() >= RADIX) { this->push_up(this->digits.back() / RADIX); this->digits.at(this->digits.size() - 2) %= RADIX; } } } using namespace mlib; Int::Int(): Int("0") { } Int::Int(const DIGITS &digits) { this->sign = PLUS; this->digits = digits; } Int::Int(const std::string &x) { std::string num = x; this->sign = PLUS; if(num.size() != 0) { if(num.front() == '+') num.erase(num.begin()); else if(num.front() == '-') { num.erase(num.begin()); this->sign = MINUS; } } if(num.size() == 0) num = "0"; while(num.size() > MAX_DIGITNUM) { push_up(std::stoull(num.substr(num.size() - MAX_DIGITNUM, MAX_DIGITNUM))); for(size_t i = 0; i < MAX_DIGITNUM; i++) num.pop_back(); } push_up(std::stoull(num)); } void Int::add(const Int &x) noexcept { if(x == 0) return; if(size() < x.size()) resize(x.size()); u64 buf, carry = 0; u64 rd; for(size_t i = 0; i < size(); i++) { rd = (i < x.size())? x.digits.at(i) : 0; buf = this->digits.at(i) + rd + carry; carry = buf / RADIX; this->digits.at(i) = buf % RADIX; } if(carry != 0) push_up(carry); normalize(); } SIGN Int::sub(const Int &x) noexcept { if(is_equal(x)) { to_zero(); return PLUS; } u32 borrow = 0; u64 rd; // *this - x if(greater_than(x)) { for(size_t i = 0; i < size(); i++) { rd = (i < x.size())? x.digits.at(i) : 0; if(this->digits.at(i) >= rd + borrow) { this->digits.at(i) = this->digits.at(i) - borrow - rd; borrow = 0; } else { this->digits.at(i) = this->digits.at(i) + RADIX - borrow - rd; borrow = 1; } } normalize(); return PLUS; } // x - *this if(size() < x.size()) resize(x.size()); for(size_t i = 0; i < x.size(); i ++) { rd = (i < size())? this->digits.at(i) : 0; if(x.digits.at(i) >= rd + borrow) { this->digits.at(i) = x.digits.at(i) - borrow - rd; borrow = 0; } else { this->digits.at(i) = x.digits.at(i) + RADIX - borrow - rd; borrow = 1; } } normalize(); return MINUS; } void Int::mul(const Int &x) noexcept { if(x == 0) { to_zero(); return; } // adjust the number of digits to 2^n size_t n = 1; while (n < size() + x.size()) n <<= 1; std::vector> a(n); for(size_t i = 0; i < n; i++) { a.at(i).real((i >= size()) ? 0 : this->digits.at(i)); a.at(i).imag((i >= x.size()) ? 0 : x.digits.at(i)); } a = FFT::fft(a); std::vector> res(n); for(size_t i = 0; i < n; i++) { res.at(i) = (a.at(i) + std::conj(a.at((n - i) % n))) * 0.5 * (a.at(i) - std::conj(a.at((n - i) % n))) * std::complex(0, -0.5); } res = FFT::ifft(res); resize(n); for(size_t i = 0; i < n; i++) this->digits.at(i) = std::round(res.at(i).real() / static_cast(n)); normalize(); align(); } // short divisor // x is absolute value void Int::div_short(const Int &x, bool rem) noexcept { if(x < RADIX) { u64 dividend = this->digits.back(); for(u64 i = this->digits.size(); i --> 0;) { this->digits.at(i) = dividend / x.at(0); if(i == 0) { if(rem) assignment(dividend % x.at(0)); else normalize(); return; } dividend = (dividend % x.at(0)) * RADIX + this->digits.at(i - 1); } } u64 radix_per_10 = RADIX / 10; Int dividend(this->digits.back() / radix_per_10); u64 qq, xx; Int rr; for(u64 i = this->digits.size(); i --> 0;) { xx = 0; for(u64 j = MAX_DIGITNUM; j --> 0;) { qq = 0; rr = dividend; while(rr >= x) { rr -= x; qq++; } xx += qq * std::pow(RADIX_BASE, j); if(i == 0 && j == 0) { this->digits.at(i) = xx; if(!rem) { this->digits.at(i) = xx; normalize(); } else { assignment(rr); } return; } if(qq != 0) dividend = rr; dividend *= RADIX_BASE; if(j == 0) dividend += at(i - 1) / radix_per_10; else dividend += at(i) / static_cast(std::pow(RADIX_BASE, j - 1)) % 10; } this->digits.at(i) = xx; } } // Knuth D void Int::div(const Int &x, bool rem) noexcept { assert(x != 0); if(less_than(x)) { if(!rem) to_zero(); return; } if(is_equal(x)) { if(!rem) assignment(1); else assignment(0); return; } i64 s, t; s = size() - 1, t = x.size() - 1; if(t <= 1) { div_short(x.abs(), rem); return; } u64 k = RADIX / (x.digits.back() + 1); Int a = abs() * k; Int b = x.abs() * k; DIGITS q(a.size(), 0); i64 u; u64 qp, rp; u64 fi = 1; while(true) { s = a.size() - 1 + fi; a.push_up(0); u = (a.at(s) < b.at(t))? s - t - 1 : s - t; qp = (a.at(s) * RADIX + a.at(s - 1)) / b.at(t); rp = (a.at(s) * RADIX + a.at(s - 1)) % b.at(t); while(rp < RADIX && qp * b.at(t - 1) > RADIX * rp + a.at(s - 2)) { qp--; rp += b.at(t); } q.at(u) = qp; Int xx; xx.to_radix_power(u); xx *= b; a -= qp * xx; if(a < 0) { --q.at(u); a += xx; } fi = 0; if(u == 0) { if(!rem) { this->digits = q; normalize(); } else { assignment(a / k); } return; } } } bool Int::is_equal(const Int &x) const noexcept { if(size() != x.size()) return false; for(size_t i = 0; i < size(); i++) { if(this->digits.at(i) != x.digits.at(i)) return false; } return true; } bool Int::less_than(const Int &x, bool equal) const noexcept { if(size() < x.size()) return true; if(size() > x.size()) return false; for(size_t i = size(); i --> 0;) { if(this->digits.at(i) < x.digits.at(i)) return true; if(this->digits.at(i) > x.digits.at(i)) return false; } // equal return equal; } bool Int::greater_than(const Int &x, bool equal) const noexcept { return !less_than(x, !equal); } void Int::a_add(const Int &x) noexcept { if(this->sign == x.sign) { add(x); } else { if(sub(x) == MINUS) this->sign = x.sign; } } void Int::a_sub(const Int &x) noexcept { a_add(-x); } void Int::a_mul(const Int &x) noexcept { mul(x); this->sign = !(this->sign ^ x.sign); } void Int::a_div(const Int &x) noexcept { div(x); this->sign = !(this->sign ^ x.sign); } void Int::a_mod(const Int &x) noexcept { div(x, true); } bool Int::a_is_equal(const Int &x) const noexcept { if(is_equal(0) && x.is_equal(0)) return true; if(this->sign != x.sign) return false; return is_equal(x); } bool Int::a_less_than(const Int &x, bool equal) const noexcept { // +, - zero if(is_equal(0)) { if(x.sign == PLUS) return less_than(x, equal); else return greater_than(x, equal); } // different sign if(!x.is_equal(0)) { if(this->sign == MINUS && x.sign == PLUS) return true; if(this->sign == PLUS && x.sign == MINUS) return false; } // same sign if(this->sign == PLUS) return less_than(x, equal); return greater_than(x, equal); } bool Int::a_greater_than(const Int &x, bool equal) const noexcept { return !a_less_than(x, !equal); } namespace mlib { namespace random { // return random Int value in [0, x) Int randint(const Int &x); } } mlib::Int mlib::random::randint(const mlib::Int &x) { assert(x.size() != 0); std::random_device seed; std::mt19937 mt(seed()); std::uniform_int_distribution<> rand(0, mlib::RADIX - 1); mlib::DIGITS dig(x.size()); for(u64 i = 0; i < dig.size() - 1; i++) dig[i] = rand(mt); std::uniform_int_distribution<> rand2(0, x.most_sig() - 1); dig[dig.size() - 1] = rand2(mt); mlib::Int ret(dig); ret.normalize(); return ret; } namespace mlib { bool is_prime(const Int &n); } // Miller-Rabin algorithm bool mlib::is_prime(const mlib::Int &n) { if(n == 2) return true; if(n == 1) return false; if(n % 2 == 0) return false; mlib::Int d = (n - 1) / 2; while(d % 2 == 0) d /= 2; for(u64 i = 0; i < 100; i++) { auto a = mlib::random::randint(n - 2) + 1; auto x = mlib::mod_pow(a, d, n); auto t = d; while(t != n - 1 && x != 1 && x != n - 1) { x = mlib::mod_pow(x, mlib::Int(2), n); t *= 2; } if(x != n - 1 && x % 2 == 0) return false; } return true; } int main() { u64 n; std::cin >> n; for(u64 i = 0; i < n; i++) { mlib::Int x; std::cin >> x; std::cout << x << " "; if(mlib::is_prime(x)) std::cout << 1 << std::endl; else std::cout << 0 << std::endl; } return 0; }