#include namespace suisen { template bool chmin(T& x, const T& y) { return y >= x ? false : (x = y, true); } template bool chmax(T& x, const T& y) { return y <= x ? false : (x = y, true); } template constexpr int pow_m1(T n) { return -(n & 1) | 1; } template constexpr T fld(const T x, const T y) { T q = x / y, r = x % y; return q - ((x ^ y) < 0 and (r != 0)); } template constexpr T cld(const T x, const T y) { T q = x / y, r = x % y; return q + ((x ^ y) > 0 and (r != 0)); } } namespace suisen::macro { #define IMPL_REPITER(cond) auto& begin() { return *this; } auto end() { return nullptr; } auto& operator*() { return _val; } auto& operator++() { return _val += _step, *this; } bool operator!=(std::nullptr_t) { return cond; } template == std::is_signed_v), std::nullptr_t> = nullptr> struct rep_impl { Int _val; const Int _end, _step; rep_impl(Int n) : rep_impl(0, n) {} rep_impl(IntL l, Int r, IntStep step = 1) : _val(l), _end(r), _step(step) {} IMPL_REPITER((_val < _end)) }; template == std::is_signed_v), std::nullptr_t> = nullptr> struct rrep_impl { Int _val; const Int _end, _step; rrep_impl(Int n) : rrep_impl(0, n) {} rrep_impl(IntL l, Int r) : _val(r - 1), _end(l), _step(-1) {} rrep_impl(IntL l, Int r, IntStep step) : _val(l + fld(r - l - 1, step) * step), _end(l), _step(-step) {} IMPL_REPITER((_val >= _end)) }; template struct repinf_impl { Int _val; const Int _step; repinf_impl(Int l, IntStep step = 1) : _val(l), _step(step) {} IMPL_REPITER((true)) }; #undef IMPL_REPITER } #include #include #include namespace suisen { template using constraints_t = std::enable_if_t, std::nullptr_t>; template struct bitnum { static constexpr int value = 0; }; template struct bitnum>> { static constexpr int value = std::numeric_limits>::digits; }; template static constexpr int bitnum_v = bitnum::value; template struct is_nbit { static constexpr bool value = bitnum_v == n; }; template static constexpr bool is_nbit_v = is_nbit::value; template struct safely_multipliable { using type = T; }; template struct safely_multipliable, is_nbit>> { using type = long long; }; template struct safely_multipliable, is_nbit>> { using type = __int128_t; }; template struct safely_multipliable, is_nbit>> { using type = unsigned long long; }; template struct safely_multipliable, is_nbit>> { using type = __uint128_t; }; template using safely_multipliable_t = typename safely_multipliable::type; template struct rec_value_type { using type = T; }; template struct rec_value_type> { using type = typename rec_value_type::type; }; template using rec_value_type_t = typename rec_value_type::type; template class is_iterable { template static auto test(T_ e) -> decltype(e.begin(), e.end(), std::true_type{}); static std::false_type test(...); public: static constexpr bool value = decltype(test(std::declval()))::value; }; template static constexpr bool is_iterable_v = is_iterable::value; template class is_writable { template static auto test(T_ e) -> decltype(std::declval() << e, std::true_type{}); static std::false_type test(...); public: static constexpr bool value = decltype(test(std::declval()))::value; }; template static constexpr bool is_writable_v = is_writable::value; template class is_readable { template static auto test(T_ e) -> decltype(std::declval() >> e, std::true_type{}); static std::false_type test(...); public: static constexpr bool value = decltype(test(std::declval()))::value; }; template static constexpr bool is_readable_v = is_readable::value; } // namespace suisen namespace suisen::io { template >, std::negation>>>, std::nullptr_t> = nullptr> struct InputStream { private: using istream_type = std::remove_reference_t; IStream is; struct { InputStream* is; template operator T() { T e; *is >> e; return e; } } _reader{ this }; public: template InputStream(IStream_ &&is) : is(std::move(is)) {} template InputStream(IStream_ &is) : is(is) {} template InputStream& operator>>(T& e) { if constexpr (suisen::is_readable_v) is >> e; else _read(e); return *this; } auto read() { return _reader; } template void read(Head& head, Tail &...tails) { ((*this >> head) >> ... >> tails); } istream_type& get_stream() { return is; } private: static __uint128_t _stou128(const std::string& s) { __uint128_t ret = 0; for (char c : s) if ('0' <= c and c <= '9') ret = 10 * ret + c - '0'; return ret; } static __int128_t _stoi128(const std::string& s) { return (s[0] == '-' ? -1 : +1) * _stou128(s); } void _read(__uint128_t& v) { v = _stou128(std::string(_reader)); } void _read(__int128_t& v) { v = _stoi128(std::string(_reader)); } template void _read(std::pair& a) { *this >> a.first >> a.second; } template void _read(std::tuple& a) { if constexpr (N < sizeof...(Args)) *this >> std::get(a), _read(a); } template , std::nullptr_t> = nullptr> void _read(Iterable& a) { for (auto& e : a) *this >> e; } }; template InputStream(IStream &&) -> InputStream; template InputStream(IStream &) -> InputStream; InputStream cin{ std::cin }; auto read() { return cin.read(); } template void read(Head& head, Tail &...tails) { cin.read(head, tails...); } } // namespace suisen::io namespace suisen { using io::read; } // namespace suisen namespace suisen::io { template >, std::negation>>>, std::nullptr_t> = nullptr> struct OutputStream { private: using ostream_type = std::remove_reference_t; OStream os; public: template OutputStream(OStream_ &&os) : os(std::move(os)) {} template OutputStream(OStream_ &os) : os(os) {} template OutputStream& operator<<(const T& e) { if constexpr (suisen::is_writable_v) os << e; else _print(e); return *this; } void print() { *this << '\n'; } template void print(const Head& head, const Tail &...tails) { *this << head, ((*this << ' ' << tails), ...), *this << '\n'; } template , std::nullptr_t> = nullptr> void print_all(const Iterable& v, std::string sep = " ", std::string end = "\n") { for (auto it = v.begin(); it != v.end();) if (*this << *it; ++it != v.end()) *this << sep; *this << end; } ostream_type& get_stream() { return os; } private: void _print(__uint128_t value) { char buffer[41], *d = std::end(buffer); do *--d = '0' + (value % 10), value /= 10; while (value); os.rdbuf()->sputn(d, std::end(buffer) - d); } void _print(__int128_t value) { if (value < 0) *this << '-'; _print(__uint128_t(value < 0 ? -value : value)); } template void _print(const std::pair& a) { *this << a.first << ' ' << a.second; } template void _print(const std::tuple& a) { if constexpr (N < std::tuple_size_v>) { if constexpr (N) *this << ' '; *this << std::get(a), _print(a); } } template , std::nullptr_t> = nullptr> void _print(const Iterable& a) { print_all(a, " ", ""); } }; template OutputStream(OStream_ &&) -> OutputStream; template OutputStream(OStream_ &) -> OutputStream; OutputStream cout{ std::cout }, cerr{ std::cerr }; template void print(const Args &... args) { cout.print(args...); } template , std::nullptr_t> = nullptr> void print_all(const Iterable& v, const std::string& sep = " ", const std::string& end = "\n") { cout.print_all(v, sep, end); } } // namespace suisen::io namespace suisen { using io::print, io::print_all; } // namespace suisen namespace suisen { template , std::enable_if_t, std::is_invocable_r, std::invoke_result_t>>, std::nullptr_t> = nullptr> auto comparator(const ToKey& to_key, const CompKey& comp_key = std::less<>()) { return [=](const T& x, const T& y) { return comp_key(to_key(x), to_key(y)); }; } template , std::nullptr_t> = nullptr> std::vector sorted_indices(int n, const Compare& compare) { std::vector p(n); return std::iota(p.begin(), p.end(), 0), std::sort(p.begin(), p.end(), compare), p; } template , std::nullptr_t> = nullptr> std::vector sorted_indices(int n, const ToKey& to_key) { return sorted_indices(n, comparator(to_key)); } template auto priority_queue_with_comparator(const Comparator& comparator) { return std::priority_queue, Comparator>{ comparator }; } template , std::nullptr_t> = nullptr> void sort_unique_erase(Iterable& a) { std::sort(a.begin(), a.end()), a.erase(std::unique(a.begin(), a.end()), a.end()); } template struct Dim : std::array { template Dim(const Ints& ...ns) : std::array::array{ static_cast(ns)... } {} }; template Dim(const Ints& ...) -> Dim; template auto ndvec(const Dim &ns, const T& value = {}) { if constexpr (I + 1 < D) { return std::vector(ns[I], ndvec(ns, value)); } else { return std::vector(ns[I], value); } } } namespace suisen { using int128 = __int128_t; using uint128 = __uint128_t; template using min_priority_queue = std::priority_queue, std::greater>; template using max_priority_queue = std::priority_queue, std::less>; } namespace suisen { const std::string Yes = "Yes", No = "No", YES = "YES", NO = "NO"; } #ifdef LOCAL # define debug(...) debug_impl(#__VA_ARGS__, __VA_ARGS__) template void debug_impl(const char* s, const H& h, const Ts&... t) { suisen::io::cerr << "[\033[32mDEBUG\033[m] " << s << ": " << h, ((suisen::io::cerr << ", " << t), ..., (suisen::io::cerr << "\n")); } #else # define debug(...) void(0) #endif #define FOR(e, v) for (auto &&e : v) #define CFOR(e, v) for (const auto &e : v) #define REP(i, ...) CFOR(i, suisen::macro::rep_impl(__VA_ARGS__)) #define RREP(i, ...) CFOR(i, suisen::macro::rrep_impl(__VA_ARGS__)) #define REPINF(i, ...) CFOR(i, suisen::macro::repinf_impl(__VA_ARGS__)) #define LOOP(n) for ([[maybe_unused]] const auto& _ : suisen::macro::rep_impl(n)) #define ALL(iterable) std::begin(iterable), std::end(iterable) using namespace suisen; using namespace std; struct io_setup { io_setup(int precision = 20) { std::ios::sync_with_stdio(false), std::cin.tie(nullptr); std::cout << std::fixed << std::setprecision(precision); } } io_setup_ {}; constexpr int iinf = std::numeric_limits::max() / 2; constexpr long long linf = std::numeric_limits::max() / 2; #include #include #include #include #include #include namespace suisen { struct FunctionalGraph { struct Doubling; template struct DoublingSum; friend struct Doubling; template friend struct DoublingSum; FunctionalGraph() : FunctionalGraph(0) {} FunctionalGraph(int n) : _n(n), _nxt(n) {} FunctionalGraph(const std::vector& nxt) : _n(nxt.size()), _nxt(nxt) {} const int& operator[](int u) const { return _nxt[u]; } int& operator[](int u) { return _nxt[u]; } struct Doubling { friend struct FunctionalGraph; int query(int u, long long d) const { for (int l = _log; l >= 0; --l) if ((d >> l) & 1) u = _nxt[l][u]; return u; } struct BinarySearchResult { int v; long long step; operator std::pair() const { return std::pair{ v, step }; } }; template auto max_step(int u, Pred &&f) const { assert(f(u)); long long step = 0; for (int l = _log; l >= 0; --l) if (int nxt_u = _nxt[l][u]; f(nxt_u)) { u = nxt_u, step |= 1LL << l; } return BinarySearchResult{ u, step }; } template std::optional step_until(int u, Pred &&f) const { if (f(u)) return BinarySearchResult { u, 0 }; auto [v, step] = max_step(u, [&](int v) { return not f(v); }); v = _nxt[0][v], ++step; if (not f(v)) return std::nullopt; return BinarySearchResult{ v, step }; } private: int _n, _log; std::vector> _nxt; Doubling(const std::vector& nxt, long long max_step) : _n(nxt.size()), _log(floor_log2(max_step)), _nxt(_log + 1, std::vector(_n)) { _nxt[0] = nxt; for (int i = 1; i <= _log; ++i) for (int j = 0; j < _n; ++j) { _nxt[i][j] = _nxt[i - 1][_nxt[i - 1][j]]; } } }; template struct DoublingSum : private Doubling { friend struct FunctionalGraph; struct Result { int v; T sum; operator std::pair() const { return std::pair{ v, sum }; } }; auto query(int u, long long d) const { T sum = e(); for (int l = _log; l >= 0; --l) if ((d >> l) & 1) sum = op(sum, _dat[l][std::exchange(u, _nxt[l][u])]); return Result{ u, sum }; } struct BinarySearchResult { int v; T sum; long long step; operator std::tuple() const { return std::tuple{ v, sum, step }; } }; template auto max_step(int u, Pred &&f) const { assert(f(e())); long long step = 0; T sum = e(); for (int l = _log; l >= 0; --l) { if (T nxt_sum = op(sum, _dat[l][u]); f(nxt_sum)) { sum = std::move(nxt_sum), u = _nxt[l][u], step |= 1LL << l; } } return BinarySearchResult{ u, sum, step }; } template std::optional step_until(int u, Pred &&f) const { if (f(e())) return BinarySearchResult { u, e(), 0 }; auto [v, sum, step] = max_step(u, [&](const T& v) { return not f(v); }); sum = op(sum, _dat[0][v]), v = _nxt[0][v], ++step; if (not f(sum)) return std::nullopt; return BinarySearchResult{ v, sum, step }; } private: std::vector> _dat; DoublingSum(const std::vector& nxt, long long max_step, const std::vector& dat) : Doubling(nxt, max_step), _dat(_log + 1, std::vector(_n, e())) { _dat[0] = dat; for (int i = 1; i <= _log; ++i) for (int j = 0; j < _n; ++j) { _dat[i][j] = op(_dat[i - 1][j], _dat[i - 1][_nxt[i - 1][j]]); } } }; Doubling doubling(long long max_step) const { return Doubling(_nxt, max_step); } template DoublingSum doubling(long long max_step, const std::vector& dat) const { return DoublingSum(_nxt, max_step, dat); } struct InfinitePath { int head_v; int head_len; int loop_v; int loop_len; InfinitePath() = default; InfinitePath(int head_v, int head_len, int loop_v, int loop_len) : head_v(head_v), head_len(head_len), loop_v(loop_v), loop_len(loop_len) {} }; std::vector infinite_paths() const { std::vector res(_n); std::vector vis(_n, _n); std::vector dep(_n, 0); int time = 0; auto dfs = [&](auto dfs, int u) -> int { vis[u] = time; int v = _nxt[u]; if (vis[v] == vis[u]) { // found cycle int loop_len = dep[u] - dep[v] + 1; res[u] = { u, 0, u, loop_len }; return loop_len - 1; } else if (vis[v] < vis[u]) { res[u] = { u, res[v].head_len + 1, res[v].loop_v, res[v].loop_len }; return 0; } else { dep[v] = dep[u] + 1; int c = dfs(dfs, v); if (c > 0) { // in cycle res[u] = { u, 0, u, res[v].loop_len }; return c - 1; } else { // out of cycle res[u] = { u, res[v].head_len + 1, res[v].loop_v, res[v].loop_len }; return 0; } } }; for (int i = 0; i < _n; ++i, ++time) if (vis[i] == _n) dfs(dfs, i); return res; } /** * Calculates k'th iterate: f(f(f(...f(i)))) for all 0 <= i < N in O(N) time. * Reference: https://noshi91.hatenablog.com/entry/2019/09/22/114149 */ std::vector kth_iterate(const long long k) const { assert(k >= 0); std::vector res(_n); std::vector forest_roots; std::vector> forest(_n); std::vector>> qs(_n); for (const auto& path : infinite_paths()) { const int v = path.head_v; (path.head_len == 0 ? forest_roots : forest[_nxt[v]]).push_back(v); if (path.head_len >= k) continue; qs[path.loop_v].emplace_back(k - path.head_len, v); } std::vector dfs_path(_n); auto dfs = [&](auto dfs, int u, int d) -> void { dfs_path[d] = u; if (d >= k) res[u] = dfs_path[d - k]; for (int v : forest[u]) dfs(dfs, v, d + 1); }; for (int root : forest_roots) dfs(dfs, root, 0); std::vector seen(_n, false); for (int root : forest_roots) { if (seen[root]) continue; std::vector cycle{ root }; for (int v = _nxt[root]; v != root; v = _nxt[v]) cycle.push_back(v); const int len = cycle.size(); for (int i = 0; i < len; ++i) { const int s = cycle[i]; seen[s] = true; for (const auto& [rem, res_index] : qs[s]) { res[res_index] = cycle[(i + rem) % len]; } } } return res; } private: int _n; std::vector _nxt; static int floor_log2(long long v) { int l = 0; while (1LL << (l + 1) <= v) ++l; return l; } }; } // namespace suisen namespace suisen { class HeavyLightDecomposition { public: template using is_point_update_query = std::is_invocable; template using is_range_update_query = std::is_invocable; template using is_point_get_query = std::is_same, T>; template using is_range_fold_query = std::is_same, T>; using Graph = std::vector>; HeavyLightDecomposition() = default; HeavyLightDecomposition(Graph &g) : n(g.size()), visit(n), leave(n), head(n), ord(n), siz(n), par(n, -1), dep(n, 0) { for (int i = 0; i < n; ++i) if (par[i] < 0) dfs(g, i, -1); int time = 0; for (int i = 0; i < n; ++i) if (par[i] < 0) hld(g, i, -1, time); } HeavyLightDecomposition(Graph &g, const std::vector &roots) : n(g.size()), visit(n), leave(n), head(n), ord(n), siz(n), par(n, -1), dep(n, 0) { for (int i : roots) dfs(g, i, -1); int time = 0; for (int i : roots) hld(g, i, -1, time); } int size() const { return n; } int lca(int u, int v) const { for (;; v = par[head[v]]) { if (visit[u] > visit[v]) std::swap(u, v); if (head[u] == head[v]) return u; } } int la(int u, int k, int default_value = -1) const { if (k < 0) return default_value; while (u >= 0) { int h = head[u]; if (visit[u] - k >= visit[h]) return ord[visit[u] - k]; k -= visit[u] - visit[h] + 1; u = par[h]; } return default_value; } int jump(int u, int v, int d, int default_value = -1) const { if (d < 0) return default_value; const int w = lca(u, v); int uw = dep[u] - dep[w]; if (d <= uw) return la(u, d); int vw = dep[v] - dep[w]; return d <= uw + vw ? la(v, (uw + vw) - d) : default_value; } int dist(int u, int v) const { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; } template , std::is_invocable_r> = nullptr> T fold_path(int u, int v, T identity, F bin_op, Q fold_query, bool is_edge_query = false) const { T res = identity; for (;; v = par[head[v]]) { if (visit[u] > visit[v]) std::swap(u, v); if (head[u] == head[v]) break; res = bin_op(fold_query(visit[head[v]], visit[v] + 1), res); } return bin_op(fold_query(visit[u] + is_edge_query, visit[v] + 1), res); } template < typename T, typename Q1, typename Q2, typename F, constraints_t, is_range_fold_query, std::is_invocable_r> = nullptr > T fold_path_noncommutative(int u, int v, T identity, F bin_op, Q1 fold_query, Q2 fold_query_rev, bool is_edge_query = false) const { T res_u = identity, res_v = identity; // a := lca(u, v) // res = fold(u -> a) + fold(a -> v) while (head[u] != head[v]) { if (visit[u] < visit[v]) { // a -> v res_v = bin_op(fold_query(visit[head[v]], visit[v] + 1), res_v); v = par[head[v]]; } else { // u -> a res_u = bin_op(res_u, fold_query_rev(visit[head[u]], visit[u] + 1)); u = par[head[u]]; } } if (visit[u] < visit[v]) { // a = u res_v = bin_op(fold_query(visit[u] + is_edge_query, visit[v] + 1), res_v); } else { // a = v res_u = bin_op(res_u, fold_query_rev(visit[v] + is_edge_query, visit[u] + 1)); } return bin_op(res_u, res_v); } template > = nullptr> void update_path(int u, int v, Q update_query, bool is_edge_query = false) const { for (;; v = par[head[v]]) { if (visit[u] > visit[v]) std::swap(u, v); if (head[u] == head[v]) break; update_query(visit[head[v]], visit[v] + 1); } update_query(visit[u] + is_edge_query, visit[v] + 1); } template > = nullptr> T fold_subtree(int u, Q fold_query, bool is_edge_query = false) const { return fold_query(visit[u] + is_edge_query, leave[u]); } template > = nullptr> void update_subtree(int u, Q update_query, bool is_edge_query = false) const { update_query(visit[u] + is_edge_query, leave[u]); } template > = nullptr> T get_point(int u, Q get_query) const { return get_query(visit[u]); } template > = nullptr> void update_point(int u, Q update_query) const { update_query(visit[u]); } std::vector inv_ids() const { std::vector inv(n); for (int i = 0; i < n; ++i) inv[visit[i]] = i; return inv; } int get_visit_time(int u) const { return visit[u]; } int get_leave_time(int u) const { return leave[u]; } int get_head(int u) const { return head[u]; } int get_kth_visited(int k) const { return ord[k]; } int get_subtree_size(int u) const { return siz[u]; } int get_parent(int u) const { return par[u]; } int get_depth(int u) const { return dep[u]; } std::vector get_roots() const { std::vector res; for (int i = 0; i < n; ++i) if (par[i] < 0) res.push_back(i); return res; } private: int n; std::vector visit, leave, head, ord, siz, par, dep; int dfs(Graph &g, int u, int p) { par[u] = p; siz[u] = 1; int max_size = 0; for (int &v : g[u]) { if (v == p) continue; dep[v] = dep[u] + 1; siz[u] += dfs(g, v, u); if (max_size < siz[v]) { max_size = siz[v]; std::swap(g[u].front(), v); } } return siz[u]; } void hld(Graph &g, int u, int p, int &time) { visit[u] = time, ord[time] = u, ++time; head[u] = p >= 0 and g[p].front() == u ? head[p] : u; for (int v : g[u]) { if (v != p) hld(g, v, u, time); } leave[u] = time; } }; } // namespace suisen #include #include void solve() { int n, L, R; read(n, L, R); vector a(n); read(a); for (auto &e : a) --e; FunctionalGraph g(a); auto paths = g.infinite_paths(); atcoder::dsu uf(n); vector ss; REP(i, n) { int j = a[i]; if (uf.same(i, j)) { ss.push_back(j); } else { uf.merge(i, j); } } vector pos(n, -1); atcoder::dsu uf_cycle(n); set> ce; vector cv; for (int s : ss) { int id = 0; for (int v = s;;) { pos[v] = id++; cv.push_back(v); ce.emplace(v, a[v]); uf_cycle.merge(v, a[v]); v = a[v]; if (v == s) break; } } vector> t(n); REP(i, n) { int j = a[i]; if (ce.count({ i, j })) { continue; } t[j].push_back(i); } vector d(n); HeavyLightDecomposition hld(t, cv); for (int r : cv) { auto dfs = [&](auto dfs, int u) -> void { for (int v : t[u]) { d[v] = d[u] + 1; dfs(dfs, v); } }; dfs(dfs, r); } auto f = [&](int u, int v) -> pair { if (paths[u].loop_v != paths[v].loop_v) { if (paths[v].loop_v != v) return { -1, -1 }; int x = paths[u].loop_v; if (not uf_cycle.same(x, v)) return { -1, -1 }; // u -> x // x -> v int a = d[u]; int px = pos[x]; int pv = pos[v]; int l = paths[x].loop_len; if (px <= pv) { a += pv - px; } else { a += pv - px + l; } return { a, l }; } int tu = hld.get_visit_time(u); int tvl = hld.get_visit_time(v); int tvr = hld.get_leave_time(v); if (tvl <= tu and tu < tvr) { return { d[u] - d[v], paths[v].loop_v != v ? iinf : paths[v].loop_len }; } return { -1, -1 }; }; int q; read(q); LOOP(q) { int s, t; read(s, t); --s, --t; auto [a, b] = f(s, t); if (a == -1) { print(-1); continue; } assert(a > 0 and b > 0); if (b == iinf) { // kL <= a <= kR int kl = fld(a, L), kr = cld(a, R); print(kr <= kl ? kr : -1); continue; } // xL <= A+yB <= xR constexpr long long INF = 10000000; long long xmin = cld(a, R); long long offset_sumR = atcoder::floor_sum(xmin, b, R, -a); long long offset_sumL = atcoder::floor_sum(xmin, b, L, -(a + 1)); long long ng = cld(a, R) - 1, ok = INF; while (ok - ng > 1) { long long x = (ok + ng) / 2; long long sumR = atcoder::floor_sum(x + 1, b, R, -a) - offset_sumR; long long sumL = atcoder::floor_sum(x + 1, b, L, -(a + 1)) - offset_sumL; (sumL < sumR ? ok : ng) = x; } print(ok == INF ? -1 : ok); } } int main() { int test_case_num = 1; // read(test_case_num); LOOP(test_case_num) solve(); return 0; }