#line 1 "/home/anqooqie/.proconlib/tools/util.hpp" // To see the details of my library, visit my GitHub Pages. // https://anqooqie.github.io/proconlib/ #ifdef LOCAL #ifndef _GLIBCXX_DEBUG #define _GLIBCXX_DEBUG #endif #else #ifndef NDEBUG #define NDEBUG #endif #endif #include #line 1 "/home/anqooqie/.proconlib/tools/resize.hpp" #line 5 "/home/anqooqie/.proconlib/tools/resize.hpp" // Source: https://koyumeishi.hatenablog.com/entry/2016/02/01/152426 // License: unknown // Author: koyumeishi namespace tools { template void resize(::std::vector& vector, const Head& head) { vector.resize(head); } template void resize(::std::vector& vector, const Head& head, const Tail&... tail) { vector.resize(head); for (auto& child : vector) { ::tools::resize(child, tail...); } } } #line 1 "/home/anqooqie/.proconlib/tools/fill.hpp" #line 5 "/home/anqooqie/.proconlib/tools/fill.hpp" #include #line 1 "/home/anqooqie/.proconlib/tools/is_range.hpp" #line 7 "/home/anqooqie/.proconlib/tools/is_range.hpp" namespace tools { template class is_range { private: template static auto check(U x) -> decltype(::std::begin(x), ::std::end(x), ::std::true_type{}); static ::std::false_type check(...); public: static const bool value = decltype(check(::std::declval()))::value; }; } #line 9 "/home/anqooqie/.proconlib/tools/fill.hpp" namespace tools { template auto fill(::std::vector& vector, const V& value) -> ::std::enable_if_t::value, void> { ::std::fill(::std::begin(vector), ::std::end(vector), value); } template auto fill(::std::vector& vector, const V& value) -> ::std::enable_if_t<::tools::is_range::value, void> { for (auto& child : vector) { ::tools::fill(child, value); } } } #line 20 "/home/anqooqie/.proconlib/tools/util.hpp" using i64 = ::std::int_fast64_t; using u64 = ::std::uint_fast64_t; using i32 = ::std::int_fast32_t; using u32 = ::std::uint_fast32_t; #define ALL(x) ::std::begin((x)), ::std::end((x)) namespace tools { namespace detail { namespace util { template class has_val { private: template static auto check(U x) -> decltype(x.val(), ::std::true_type{}); static ::std::false_type check(...); public: static const bool value = decltype(check(::std::declval()))::value; }; template ::std::istream& read(::std::istream& is, T& container) { for (auto& v : container) { is >> v; } return is; } template ::std::ostream& debug_print(::std::ostream& os, const T& container) { ::std::string delimiter = ""; os << '['; for (const auto& v : container) { os << delimiter << v; delimiter = ", "; } os << ']'; return os; } } } } template ::std::istream& operator>>(::std::istream& is, ::std::vector& vector) { return ::tools::detail::util::read(is, vector); } template ::std::istream& operator>>(::std::istream& is, ::std::array& array) { return ::tools::detail::util::read(is, array); } template ::std::ostream& operator<<(::std::ostream& os, const ::std::vector& vector) { return ::tools::detail::util::debug_print(os, vector); } template ::std::ostream& operator<<(::std::ostream& os, const ::std::array& array) { return ::tools::detail::util::debug_print(os, array); } template ::std::ostream& operator<<(::std::ostream& os, const ::std::unordered_set& unordered_set) { return ::tools::detail::util::debug_print(os, unordered_set); } template ::std::ostream& operator<<(::std::ostream& os, ::std::stack& stack) { ::std::stack other; ::std::string delimiter = ""; os << '['; while (!stack.empty()) { os << delimiter << stack.top(); delimiter = ", "; other.push(stack.top()); stack.pop(); } os << ']'; while (!other.empty()) { stack.push(other.top()); other.pop(); } return os; } template ::std::ostream& operator<<(::std::ostream& os, ::std::queue& queue) { ::std::queue other = queue; ::std::string delimiter = ""; os << '['; while (!queue.empty()) { os << delimiter << queue.front(); delimiter = ", "; queue.pop(); } os << ']'; queue = ::std::move(other); return os; } template ::std::ostream& operator<<(::std::ostream& os, const ::std::pair& pair) { return os << '[' << pair.first << ", " << pair.second << ']'; } template ::std::ostream& operator<<(::std::ostream& os, const ::std::tuple& tuple) { if constexpr (I == -1) { os << '['; } else if constexpr (I == int(sizeof...(Args))) { os << ']'; } else if constexpr (I == 0) { os << ::std::get(tuple); } else { os << ", " << ::std::get(tuple); } if constexpr (I < int(sizeof...(Args))) { return operator<<(os, tuple); } else { return os; } } template auto operator<<(::std::ostream& os, const T& x) -> ::std::enable_if_t<::tools::detail::util::has_val::value, ::std::ostream&> { return os << x.val(); } #line 1 "/home/anqooqie/.proconlib/tools/tetration_mod.hpp" #line 1 "/home/anqooqie/.proconlib/tools/pow.hpp" #line 1 "/home/anqooqie/.proconlib/tools/monoid.hpp" #line 7 "/home/anqooqie/.proconlib/tools/monoid.hpp" namespace tools { namespace monoid { template ::min()> struct max { using T = Type; static T op(const T lhs, const T rhs) { return ::std::max(lhs, rhs); } static T e() { return E; } }; template ::max()> struct min { using T = Type; static T op(const T lhs, const T rhs) { return ::std::min(lhs, rhs); } static T e() { return E; } }; template struct multiplies { using T = Type; static T op(const T lhs, const T rhs) { return lhs * rhs; } static T e() { return T(1); } }; template struct gcd { using T = Type; static T op(const T lhs, const T rhs) { return ::std::gcd(lhs, rhs); } static T e() { return T(0); } }; template struct update { using T = Type; static T op(const T lhs, const T rhs) { return lhs == E ? rhs : lhs; } static T e() { return E; } }; } } #line 1 "/home/anqooqie/.proconlib/tools/square.hpp" #line 5 "/home/anqooqie/.proconlib/tools/square.hpp" namespace tools { template typename M::T square(const typename M::T& x) { return M::op(x, x); } template T square(const T& x) { return ::tools::square<::tools::monoid::multiplies>(x); } } #line 7 "/home/anqooqie/.proconlib/tools/pow.hpp" namespace tools { template typename M::T pow(const typename M::T& base, const ::std::size_t& exponent) { return exponent == 0 ? M::e() : exponent % 2 == 0 ? ::tools::square(::tools::pow(base, exponent / 2)) : M::op(::tools::pow(base, exponent - 1), base); } template T pow(const T& base, const ::std::size_t& exponent) { return ::tools::pow<::tools::monoid::multiplies>(base, exponent); } } #line 1 "/home/anqooqie/.proconlib/tools/prime_factorization.hpp" #line 1 "/home/anqooqie/.proconlib/tools/is_prime.hpp" #line 1 "/home/anqooqie/.proconlib/tools/prod_mod.hpp" namespace tools { template constexpr T3 prod_mod(const T1 x, const T2 y, const T3 m) { using u128 = unsigned __int128; u128 prod_mod = u128(x >= 0 ? x : -x) * u128(y >= 0 ? y : -y) % u128(m); if (((x >= 0) ^ (y >= 0)) == 1) prod_mod = u128(m) - prod_mod; return prod_mod; } } #line 1 "/home/anqooqie/.proconlib/tools/pow_mod.hpp" #line 1 "/home/anqooqie/.proconlib/tools/mod.hpp" #line 1 "/home/anqooqie/.proconlib/tools/quo.hpp" #line 5 "/home/anqooqie/.proconlib/tools/quo.hpp" namespace tools { template constexpr ::std::common_type_t quo(const M lhs, const N rhs) { if (lhs >= 0) { return lhs / rhs; } else { if (rhs >= 0) { return -((-lhs - 1 + rhs) / rhs); } else { return (-lhs - 1 + -rhs) / -rhs; } } } } #line 6 "/home/anqooqie/.proconlib/tools/mod.hpp" namespace tools { template constexpr ::std::common_type_t mod(const M lhs, const N rhs) { if constexpr (::std::is_unsigned_v && ::std::is_unsigned_v) { return lhs % rhs; } else { return lhs - ::tools::quo(lhs, rhs) * rhs; } } } #line 6 "/home/anqooqie/.proconlib/tools/pow_mod.hpp" namespace tools { template constexpr T3 pow_mod(const T1 x, T2 n, const T3 m) { if (m == 1) return 0; T3 r = 1; T3 y = ::tools::mod(x, m); while (n > 0) { if ((n & 1) > 0) { r = ::tools::prod_mod(r, y, m); } y = ::tools::prod_mod(y, y, m); n /= 2; } return r; } } #line 8 "/home/anqooqie/.proconlib/tools/is_prime.hpp" namespace tools { constexpr bool is_prime(const ::std::uint_fast64_t n) { constexpr ::std::array<::std::uint_fast64_t, 7> bases = {2, 325, 9375, 28178, 450775, 9780504, 1795265022}; if (n <= 1) return false; if (n == 2) return true; if (n % 2 == 0) return false; ::std::uint_fast64_t d = n - 1; for (; d % 2 == 0; d /= 2); for (const ::std::uint_fast64_t a : bases) { if (a % n == 0) return true; ::std::uint_fast64_t power = d; ::std::uint_fast64_t target = ::tools::pow_mod(a, power, n); bool is_composite = true; if (target == 1) is_composite = false; for (; is_composite && power != n - 1; power *= 2, target = ::tools::prod_mod(target, target, n)) { if (target == n - 1) is_composite = false; } if (is_composite) { return false; } } return true; } } #line 12 "/home/anqooqie/.proconlib/tools/prime_factorization.hpp" namespace tools { template ::std::map prime_factorization(T n) { assert(1 <= n && n <= 1000000000000000000); ::std::map result; for (; n % 2 == 0; n /= 2) { ++result[2]; } if (n == 1) return result; ::std::minstd_rand engine; ::std::queue factors({n}); while (!factors.empty()) { const T factor = factors.front(); factors.pop(); if (::tools::is_prime(factor)) { ++result[factor]; } else { ::std::uniform_int_distribution dist(1, factor - 2); while (true) { T c = dist(engine); if (c == factor - 2) c = factor - 1; T x = 2; T y = 2; T d = 1; while (d == 1) { x = ::tools::prod_mod(x, x, factor); x += c; if (x >= factor) x -= factor; y = ::tools::prod_mod(y, y, factor); y += c; if (y >= factor) y -= factor; y = ::tools::prod_mod(y, y, factor); y += c; if (y >= factor) y -= factor; d = ::std::gcd(::std::abs(x - y), factor); } if (d < factor) { factors.push(d); factors.push(factor / d); break; } } } } return result; } } #line 1 "/home/anqooqie/.proconlib/tools/totient.hpp" #line 7 "/home/anqooqie/.proconlib/tools/totient.hpp" namespace tools { template T totient(const T& x) { assert(1 <= x && x <= 1000000000000000000); T prod = 1; for (const auto& [distinct_prime_factor, exponent] : ::tools::prime_factorization(x)) { prod *= ::tools::pow(distinct_prime_factor, exponent - 1) * (distinct_prime_factor - 1); } return prod; } } #line 1 "/home/anqooqie/.proconlib/tools/garner.hpp" #include #line 1 "/home/anqooqie/.proconlib/tools/inv_mod.hpp" #line 1 "/home/anqooqie/.proconlib/tools/extgcd.hpp" #line 6 "/home/anqooqie/.proconlib/tools/extgcd.hpp" namespace tools { template ::std::tuple extgcd(T prev_r, T r) { T prev_s = 1; T prev_t = 0; T s = 0; T t = 1; while (r != 0) { const T q = ::tools::quo(prev_r, r); const T next_r = prev_r - q * r; prev_r = r; r = next_r; const T next_s = prev_s - q * s; prev_s = s; s = next_s; const T next_t = prev_t - q * t; prev_t = t; t = next_t; } if (prev_r < T(0)) prev_r = -prev_r; return {prev_s, prev_t, prev_r}; } } #line 7 "/home/anqooqie/.proconlib/tools/inv_mod.hpp" namespace tools { template constexpr T2 inv_mod(const T1 x, const T2 m) { const auto [x0, y0, gcd] = ::tools::extgcd(x, m); assert(gcd == 1); return ::tools::mod(x0, m); } } #line 13 "/home/anqooqie/.proconlib/tools/garner.hpp" // Source: https://qiita.com/drken/items/ae02240cd1f8edfc86fd // License: unknown // Author: drken namespace tools { template ::std::optional<::std::pair<::std::int_fast64_t, ::std::int_fast64_t>> garner(const Iterator& begin, const Iterator& end, const ModType& mod) { ::std::vector<::std::int_fast64_t> b, m; for (auto it = begin; it != end; ++it) { b.push_back(::tools::mod(it->first, it->second)); m.push_back(it->second); } ::std::int_fast64_t lcm = 1; for (::std::size_t i = 0; i < b.size(); ++i) { for (::std::size_t j = 0; j < i; ++j) { ::std::int_fast64_t g = ::std::gcd(m[i], m[j]); if ((b[i] - b[j]) % g != 0) return ::std::nullopt; m[i] /= g; m[j] /= g; ::std::int_fast64_t gi = ::std::gcd(m[i], g), gj = g / gi; do { g = ::std::gcd(gi, gj); gi *= g, gj /= g; } while (g != 1); m[i] *= gi, m[j] *= gj; b[i] %= m[i], b[j] %= m[j]; } } for (::std::size_t i = 0; i < b.size(); ++i) { (lcm *= m[i]) %= mod; } m.push_back(mod); ::std::vector<::std::int_fast64_t> coeffs(m.size(), 1); ::std::vector<::std::int_fast64_t> constants(m.size(), 0); for (::std::size_t k = 0; k < b.size(); ++k) { ::std::int_fast64_t t = ::tools::mod((b[k] - constants[k]) * ::tools::inv_mod(coeffs[k], m[k]), m[k]); for (::std::size_t i = k + 1; i < m.size(); ++i) { (constants[i] += t * coeffs[i]) %= m[i]; (coeffs[i] *= m[k]) %= m[i]; } } return ::std::make_optional<::std::pair<::std::int_fast64_t, ::std::int_fast64_t>>(constants.back(), lcm); } template ::std::optional<::std::pair> garner(const Iterator& begin, const Iterator& end) { const auto result = ::tools::garner(begin, end, M::mod()); if (!result) return ::std::nullopt; return ::std::make_optional<::std::pair>(M::raw(result->first), M::raw(result->second)); } } #line 13 "/home/anqooqie/.proconlib/tools/tetration_mod.hpp" namespace tools { template T tetration_mod(const T a, const T b, const T m) { assert(a >= 0); assert(b >= 0); assert(m >= 1); if (m == 1) return 0; // It returns min(fa^^fb, 2^63 - 1). const auto f = [](const ::std::int_fast64_t fa, const ::std::int_fast64_t fb) { if (fa == 0) return 1 - fb % 2; if (fa == 1) return ::std::int_fast64_t(1); if (fb == 0) return ::std::int_fast64_t(1); if (fb == 1) return fa; if (fb == 2 && fa <= 15) return ::tools::pow(fa, fa); if (fb == 3 && fa <= 3) return ::tools::pow(fa, ::tools::pow(fa, fa)); if (fb == 4 && fa <= 2) return ::tools::pow(fa, ::tools::pow(fa, ::tools::pow(fa, fa))); // Too large return ::std::numeric_limits<::std::int_fast64_t>::max(); }; if (f(a, b) < ::std::numeric_limits<::std::int_fast64_t>::max()) { return f(a, b) % m; } ::std::vector<::std::pair> answers; for (const auto& [p, q] : ::tools::prime_factorization(m)) { const T P = ::tools::pow(p, q); if (::std::gcd(a, p) == 1) { answers.emplace_back(::tools::pow_mod(a, ::tools::tetration_mod(a, b - 1, ::tools::totient(P)), P), P); } else { if (f(a, b - 1) >= q) { answers.emplace_back(0, P); } else { answers.emplace_back(::tools::pow_mod(a, f(a, b - 1), P), P); } } } return ::tools::garner(answers.begin(), answers.end(), m)->first; } } #line 3 "main.cpp" int main() { std::cin.tie(nullptr); std::ios_base::sync_with_stdio(false); i64 A, N, M; std::cin >> A >> N >> M; std::cout << tools::tetration_mod(A, N, M) << '\n'; return 0; }