#line 2 "/home/chabudaisanta/lib/gwen/src/gwen/my_template.hpp" #ifdef LOCAL #define _GLIBCXX_DEBUG #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #line 2 "/home/chabudaisanta/lib/gwen/src/gwen/dump.hpp" #line 17 "/home/chabudaisanta/lib/gwen/src/gwen/dump.hpp" #line 2 "/home/chabudaisanta/lib/gwen/src/gwen/types.hpp" #include namespace gwen { using i32 = int32_t; using i64 = int64_t; using i128 = __int128_t; using u32 = uint32_t; using u64 = uint64_t; using u128 = __uint128_t; } // namespace gwen #line 19 "/home/chabudaisanta/lib/gwen/src/gwen/dump.hpp" namespace gwen { //------------------------- // standard types //------------------------- constexpr inline std::string to_str() { return ""; } constexpr inline std::string to_str(bool b) { return b ? "true" : "false"; } constexpr inline std::string to_str(char c) { return std::string{c}; } constexpr inline std::string to_str(const char* cs) { return std::string{cs}; } constexpr inline std::string to_str(const std::string& str) { return str; } constexpr inline std::string to_str(std::nullptr_t) { return "nullptr"; } constexpr inline std::string to_str(u128 x) { if (x == 0) return "0"; std::string ret; while (x) { ret.push_back(static_cast('0' + (x % 10))); x /= 10; } std::reverse(ret.begin(), ret.end()); return ret; } constexpr inline std::string to_str(i128 x) { if (x < 0) return std::string{'-'} + to_str(static_cast(-x)); return to_str(static_cast(x)); } template std::string to_str(T x) { return std::to_string(x); } template std::string to_str(T f) { return std::to_string(f); } //------------------------- // prototype //------------------------- // pair / tuple template std::string to_str(const std::pair& p); template std::string to_str(const std::tuple& t); // input iterator helper template std::string to_str(Iterator begin, Iterator end, const std::string& partition = ", ", char pb = '\0', char pe = '\0'); // sequence containers template std::string to_str(const std::vector& sc); template std::string to_str(const std::array& sc); template std::string to_str(const std::deque& sc); template std::string to_str(const T (&sc)[N]); // set containers template std::string to_str(const std::set& se); template std::string to_str(const std::multiset& se); template std::string to_str(const std::unordered_set& se); template std::string to_str(const std::unordered_multiset& se); // map containers template std::string to_str(const std::map& mp); template std::string to_str(const std::multimap& mp); template std::string to_str(const std::unordered_map& mp); template std::string to_str(const std::unordered_multimap& mp); template std::string to_str_map_helper(Iterator begin, Iterator end); // user-defined template requires requires(const T& t) { gwen::to_str(t.val()); } std::string to_str(const T& t); template requires requires(const T& t) { gwen::to_str(t.dump()); } std::string to_str(const T& t); //------------------------- // implementation //------------------------- // pair / tuple template std::string to_str(const std::pair& p) { return std::string{'('} + to_str(p.first) + ", " + to_str(p.second) + ')'; } template std::string to_str(const std::tuple& t) { std::string ret{'('}; bool first = true; std::apply( [&](const auto&... args) { ((ret += (first ? "" : ", "), ret += gwen::to_str(args), first = false), ...); }, t); return ret + ')'; } template inline std::string to_str_variadic(const Args&... args) { std::string ret; std::size_t index = 0; auto process_one = [&](const auto& arg) { if (index++ > 0) { ret += ", "; } if constexpr (requires { gwen::to_str(arg); }) { ret += gwen::to_str(arg); } else { ret += "[?]"; } }; (process_one(args), ...); return ret; } // input iterator helper template std::string to_str(Iterator begin, Iterator end, const std::string& partition, char pb, char pe) { std::string ret; if (pb) ret += pb; for (auto it = begin; it != end; ++it) { if (it != begin) ret += partition; ret += to_str(*it); } if (pe) ret += pe; return ret; } // sequence containers template std::string to_str(const std::vector& sc) { return to_str(sc.begin(), sc.end(), ", ", '[', ']'); } template std::string to_str(const std::array& sc) { return to_str(sc.begin(), sc.end(), ", ", '[', ']'); } template std::string to_str(const std::deque& sc) { return to_str(sc.begin(), sc.end(), ", ", '[', ']'); } template std::string to_str(const T (&sc)[N]) { return to_str(sc.begin(), sc.end(), ", ", '[', ']'); } // set containers template std::string to_str(const std::set& se) { return to_str(se.begin(), se.end(), ", ", '{', '}'); } template std::string to_str(const std::multiset& se) { return to_str(se.begin(), se.end(), ", ", '{', '}'); } template std::string to_str(const std::unordered_set& se) { return to_str(se.begin(), se.end(), ", ", '{', '}'); } template std::string to_str(const std::unordered_multiset& se) { return to_str(se.begin(), se.end(), ", ", '{', '}'); } // map containers template std::string to_str(const std::map& mp) { return to_str_map_helper(mp.begin(), mp.end()); } template std::string to_str(const std::multimap& mp) { return to_str_map_helper(mp.begin(), mp.end()); } template std::string to_str(const std::unordered_map& mp) { return to_str_map_helper(mp.begin(), mp.end()); } template std::string to_str(const std::unordered_multimap& mp) { return to_str_map_helper(mp.begin(), mp.end()); } template std::string to_str_map_helper(Iterator begin, Iterator end) { std::string ret{'{'}; for (auto it = begin; it != end; ++it) { if (it != begin) ret += ", "; ret += '(' + to_str(it->first) + ": " + to_str(it->second) + ')'; } ret += '}'; return ret; } // user-defined template requires requires(const T& t) { gwen::to_str(t.val()); } std::string to_str(const T& t) { return gwen::to_str(t.val()); } template requires requires(const T& t) { gwen::to_str(t.dump()); } std::string to_str(const T& t) { return gwen::to_str(t.dump()); } } // namespace gwen #ifdef LOCAL #define DEBUG(...) \ std::cerr << #__VA_ARGS__ << ": " << gwen::to_str_variadic(__VA_ARGS__) \ << '\n' #define DUMP(...) \ std::cerr << #__VA_ARGS__ << ": " << gwen::to_str_variadic(__VA_ARGS__) \ << '\n' #else #define DEBUG(...) void(0) #define DUMP(...) void(0) #endif // std::string += std::string は、atcoder環境なら線形時間でやってくれそう // https://atcoder.jp/contests/abc379/submissions/69207872 #line 37 "/home/chabudaisanta/lib/gwen/src/gwen/my_template.hpp" using gwen::i128; using gwen::i32; using gwen::i64; using gwen::u128; using gwen::u32; using gwen::u64; using ll = long long; using ull = unsigned long long; #define rep(i, l, r) for (int i = (int)(l); i < (int)(r); ++i) #define rp(i, n) for (int i = 0; i < (int)(n); ++i) #define rrep(i, l, r) for (int i = (int)(r) - 1; i >= (int)(l); --i) #define all(a) a.begin(), a.end() #define rall(a) a.rbegin(), a.rend() #ifdef LOCAL #define BAR std::cerr << "----------------------------------------------\n" #define S_BAR std::cerr << "------------------\n" #else #define BAR void(0) #define S_BAR void(0) #endif constexpr std::pair mv[] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {1, 1}, {1, -1}, {-1, -1}, {-1, 1}, {0, 0}}; constexpr int dw[] = {0, 0, -1, 1, -1, -1, 1, 1, 0}; constexpr int dh[] = {-1, 1, 0, 0, -1, 1, -1, 1, 0}; constexpr int mod998 = 998244353, mod107 = 1000000007, mod109 = 1000000009, mod31 = 2147483647; constexpr ll mod61 = (1LL << 61) - 1; constexpr int iINF = (1 << 30) + 1; constexpr ll liINF = (1LL << 60) + 1; constexpr char EL = '\n', SPA = ' '; std::pair mv_to(int hi, int wi, int dir) { assert(0 <= dir && dir < 9); return std::make_pair(hi + dh[dir], wi + dw[dir]); } template inline bool chmax(T1& a, T2 b) { return (a < b ? a = b, true : false); } template inline bool chmin(T1& a, T2 b) { return (a > b ? a = b, true : false); } template inline bool isIn(T x, T l, T r) { return (l <= x) && (x < r); } template inline bool isOut(T x, T l, T r) { return (x < l) || (r <= x); } template std::ostream& operator<<(std::ostream& os, const std::vector& vec) { for (auto it = vec.begin(); it != vec.end(); it++) os << *it << (it == prev(vec.end()) ? "" : " "); return os; } template std::istream& operator>>(std::istream& is, std::vector& vec) { for (T& e : vec) is >> e; return is; } void yon(bool b) { std::cout << (b ? "Yes\n" : "No\n"); } template std::vector idxsort(const std::vector& vec, bool rev = false) { std::vector ret(vec.size()); std::iota(ret.begin(), ret.end(), 0); sort(ret.begin(), ret.end(), [&vec, &rev](int a, int b) { return (rev ? vec[a] > vec[b] : vec[a] < vec[b]); }); return ret; } template T ceil_div(T x, T y) { assert(y != 0); T d = x / y; return d * y == x ? d : d + ((x > 0) ^ (y < 0)); } template T floor_div(T x, T y) { assert(y != 0); T d = x / y; return d * y == x ? d : d - ((x < 0) ^ (y < 0)); } template T out_div(T x, T y) { assert(y != 0); T d = x / y; return d * y == x ? d : ((x > 0) == (y > 0)) ? d + 1 : d - 1; } // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3605r0.pdf template constexpr T isqrt(const T n) noexcept { if (n <= T{1}) return n; T i_current{0}, i_next{T(T{1} << ((std::bit_width(T(n - 1)) + 1) >> 1))}; do { i_current = i_next; i_next = T((i_current + n / i_current) >> 1); } while (i_next < i_current); return i_current; } template constexpr T sq(T x) { return x * x; } template constexpr T choose2(T x) { return x * (x - 1) / 2; } template constexpr T1 getBit(T1 bit, T2 i) { return bit & (static_cast(1) << i); } template constexpr T1 setBit(T1 bit, T2 i) { return bit | (static_cast(1) << i); } template constexpr T1 clearBit(T1 bit, T2 i) { return bit & ~(static_cast(1) << i); } template constexpr T1 toggleBit(T1 bit, T2 i) { return bit ^ (static_cast(1) << i); } template auto runlength(Iterator begin, Iterator end) { using ValueType = typename std::iterator_traits::value_type; using CountType = int; std::vector> ret; for (auto it = begin; it != end; ++it) { if (ret.empty() || ret.back().first != *it) ret.emplace_back(*it, 0); ret.back().second++; } return ret; } #if __has_include() #include using mint998 = atcoder::modint998244353; using mint107 = atcoder::modint1000000007; #endif #if __has_include() #include using gmp_int = mpz_class; #endif /* #if __has_include() #include #endif */ #line 2 "/home/chabudaisanta/lib/gwen/src/gwen/math/prime.hpp" #line 4 "/home/chabudaisanta/lib/gwen/src/gwen/math/prime.hpp" #line 2 "/home/chabudaisanta/lib/gwen/src/gwen/mod/modint.hpp" // https://rsk0315.hatenablog.com/entry/2022/11/27/060616 #line 4 "/home/chabudaisanta/lib/gwen/src/gwen/mod/modint.hpp" #line 2 "/home/chabudaisanta/lib/gwen/src/gwen/mod/mod.hpp" #line 4 "/home/chabudaisanta/lib/gwen/src/gwen/mod/mod.hpp" #line 6 "/home/chabudaisanta/lib/gwen/src/gwen/mod/mod.hpp" namespace gwen { i64 pow_mod(i64 x, i64 n, i64 m = 998244353) { if (x == 0) return (n ? 0 : 1); x %= m; i64 ret = 1; while (n) { if (n & 1) ret = (ret * x) % m; x = (x * x) % m; n >>= 1; } return ret; } i64 inv_mod(i64 a, i64 m = 998244353) { i64 b = m, u = 1, v = 0; while (b) { i64 t = a / b; a -= t * b; std::swap(a, b); u -= t * v; std::swap(u, v); } u %= m; if (u < 0) u += m; return u; } u64 inv_mod_64(u64 a, u64 m) { i128 s = m, t = a; i128 x = 0, y = 1; while (t) { i128 u = s / t; s -= t * u; x -= y * u; std::swap(s, t); std::swap(x, y); } // assert(s == 1); // gcd(a,m)が1でなければ逆元は存在しない return (x % m + m) % m; } } // namespace gwen #line 7 "/home/chabudaisanta/lib/gwen/src/gwen/mod/modint.hpp" namespace gwen { class dynamic_modint64 { using m64 = dynamic_modint64; static inline u64 n = 1; static inline u64 ns = 0; static inline u64 r2 = 0; static inline constexpr u64 msk = -1; u64 reduce_mul(u64 a, u64 b) const { u128 t = static_cast(a) * b; u128 m = (t * ns) & msk; // maybe unnecessary mask ? a = (m * n + t) >> 64; // t ← (t + mN) / R return a < n ? a : a - n; } u64 tr; public: static void set_mod(u64 n_) { assert(n_ < (1ull << 62)); assert(n_ & 1); n = n_; ns = n; for (int i = 0; i < 5; ++i) ns *= 2 - ns * n; assert(ns * n == 1); ns = -ns; r2 = -static_cast(n) % n; } dynamic_modint64() : tr(0) {} // need 0 <= abs(x) < RN template dynamic_modint64(T x) : tr(reduce_mul(x, r2)) {} template dynamic_modint64(T x) : tr(0) { if (x < 0) sub(m64{static_cast(-x)}); else tr = reduce_mul(x, r2); } u64 val() const { return reduce_mul(tr, 1); } // basic operation m64& sub(const m64& x) { if (tr < x.tr) tr += n; tr -= x.tr; return *this; } m64& add(const m64& x) { tr += x.tr; if (tr >= n) tr -= n; return *this; } m64& mul(const m64& x) { tr = reduce_mul(tr, x.tr); return *this; } // operator overload m64& operator+=(const m64& x) { return add(x); } m64 operator+(const m64& x) const { m64 tmp = *this; tmp.add(x); return tmp; } m64& operator-=(const m64& x) { return sub(x); } m64 operator-(const m64& x) const { m64 tmp = *this; tmp.sub(x); return tmp; } m64& operator*=(const m64& x) { return mul(x); } m64 operator*(const m64& x) const { m64 tmp = *this; tmp.mul(x); return tmp; } // operator overload (integral) template m64& operator+=(T x) { return add(m64(x)); } template m64 operator+(T x) const { return *this + m64(x); } template m64& operator-=(T x) { return sub(m64(x)); } template m64 operator-(T x) const { return *this - m64(x); } template m64& operator*=(T x) { return mul(m64(x)); } template m64 operator*(T x) const { return *this * m64(x); } template m64 pow(T x) const { assert(x >= 0); m64 ret(1); m64 tmp(*this); while (x) { if (x & 1) ret.mul(tmp); tmp.tr = reduce_mul(tmp.tr, tmp.tr); x >>= 1; } return ret; } }; } // namespace gwen #line 7 "/home/chabudaisanta/lib/gwen/src/gwen/math/prime.hpp" namespace gwen { namespace internal { struct prime_factorize_table { i32 n; std::vector p; // p[i] = 0: i is prime (or 1/0) prime_factorize_table() : n(2), p(2, 0) {} void extend() { assert(n < (1 << 30)); n <<= 1; p.resize(n, 0); for (int i = 2; i < n; ++i) if (!p[i]) { for (i64 j = i64(i) * i; j < n; j += i) { p[j] = i; } } } // if x <= max() can factorize // else cannot factorize i32 max() const { return n - 1; } }; } // namespace internal std::vector factorize(i32 x) { assert(0 < x && x < (1 << 30)); static gwen::internal::prime_factorize_table table; while (table.max() < x) table.extend(); std::vector ret; while (table.p[x] != 0) { // if p[x] = 0, x is prime ret.emplace_back(table.p[x]); x /= table.p[x]; } if (x != 1) ret.emplace_back(x); return ret; } bool miller32(u32 n) { assert(n < 4759123141u); static u64 a[3] = {2, 7, 61}; using mint = dynamic_modint64; mint::set_mod(n); u64 s = 0, d = n - 1; while (!(d & 1)) { ++s; d >>= 1; } for (int i = 0; i < 3; ++i) { if (n <= a[i]) return true; mint x{a[i]}; x = x.pow(d); if (x.val() != 1u) { u64 t; for (t = 0; t < s; ++t) { if (x.val() == n - 1) break; x *= x; } if (t == s) return false; } } return true; } bool miller64(u64 n) { static u64 a[7] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022}; using mint = dynamic_modint64; mint::set_mod(n); u64 s = 0, d = n - 1; while (!(d & 1)) { ++s; d >>= 1; } for (int i = 0; i < 7; ++i) { if (n <= a[i]) return true; mint x{a[i]}; x = x.pow(d); u64 t; if (x.val() != 1u) { for (t = 0; t < s; ++t) { if (x.val() == n - 1) break; x *= x; } if (t == s) return false; } } return true; } bool is_prime_small(i32 n) { static gwen::internal::prime_factorize_table table; while (table.max() < n) table.extend(); return !table.p[n]; } bool miller(u64 n) { if (n <= 1) return false; else if (n == 2) return true; else if (!(n & 1)) return false; else if (n < (1u << 23)) return is_prime_small(n); else if (n < 4759123141u) return miller32(n); else return miller64(n); } } // namespace gwen #line 3 "a.cpp" using namespace std; void solve() { i32 N; cin >> N; vector M(N); cin >> M; vector> F(N); u32 grundy = 0; rp(i,N) { F[i] = gwen::factorize(M[i]); DUMP(F[i]); map cnt; for(i32 f : F[i]) cnt[f]++; u32 g = 0; for(auto [k, v] : cnt) { g ^= v % 3; } grundy ^= g; DUMP(g); } cout << (grundy ? "Alice" : "Bob") << EL; } int main(void) { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); std::cout << std::fixed << std::setprecision(16); int t = 1; // cin >> t; while (t--) solve(); std::cerr << "execution time: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "(ms)" << EL; return 0; }