結果

問題 No.2242 Cities and Teleporters
ユーザー qwewe
提出日時 2025-05-14 12:58:29
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
AC  
実行時間 1,574 ms / 3,000 ms
コード長 10,915 bytes
コンパイル時間 1,382 ms
コンパイル使用メモリ 124,416 KB
実行使用メモリ 48,760 KB
最終ジャッジ日時 2025-05-14 13:00:08
合計ジャッジ時間 24,299 ms
ジャッジサーバーID
(参考情報)
judge4 / judge3
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 26
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <cmath> // Include for ceil, log2
#include <stdexcept> // For runtime_error

using namespace std;

// Use long long for altitude/threshold as they can be up to 10^9
using ll = long long;

// Structure to store city data along with original index
// Although original ID is not directly used in the algorithm, keeping it might be useful for debugging.
struct City {
    int id; // Original 1-based ID
    ll h; 
    ll t;
};

// Comparator for sorting cities by altitude H
bool compareCities(const City& a, const City& b) {
    return a.h < b.h;
}

// Global constants and data structures for binary lifting approach
const int LOGN_MAX = 19; // Max log2(2e5) is approx 17.6. Use 19 to be safe.
map<ll, int> value_to_idx; // Map T values to dense indices 0..V-1
vector<ll> idx_to_value; // Map dense indices back to T values
vector<vector<int>> jump; // jump[p][idx] stores the index of f^(2^p)(value at idx)

vector<ll> H_sorted; // Store sorted H values for efficient lookup
vector<ll> PMT; // Prefix maximum T values for cities sorted by altitude

int N_global; // Store N globally for convenience in helper functions

/**
 * @brief Finds the count of cities with altitude <= val. This corresponds to the 1-based index `k`
 *        in the list of cities sorted by altitude such that the k-th city has the maximum altitude <= val.
 * @param val The altitude threshold.
 * @return The count of cities with altitude <= val. Returns 0 if no city satisfies the condition.
 */
int find_idx(ll val) {
    // `upper_bound` finds the first element in H_sorted strictly greater than val.
    auto it = upper_bound(H_sorted.begin(), H_sorted.end(), val);
    // The distance from the beginning to this iterator is the count of elements <= val.
    return distance(H_sorted.begin(), it); 
}

/**
 * @brief Calculates T_max(val) = max { T_j | H_j <= val }.
 *        This is the maximum teleporter threshold among all cities j whose altitude H_j is at most val.
 * @param val The altitude threshold.
 * @return The maximum T value found. Returns -1 if no city has altitude <= val.
 */
ll calculate_T_max(ll val) {
    // Find the count k' of cities with H <= val.
    int k_prime = find_idx(val);
    if (k_prime == 0) {
        // No city satisfies H_j <= val. Conceptually, T_max is -infinity. We represent this with -1.
        return -1; 
    }
    // PMT[k] stores the max T value among the first k cities sorted by altitude.
    // Since k_prime is the count, it's the correct 1-based index for PMT.
    return PMT[k_prime]; 
}

/**
 * @brief Applies the state transition function f(M) = max(M, T_max(M)).
 *        This function computes the maximum teleporter threshold reachable in the next step,
 *        given the current maximum threshold M.
 * @param M_val The current maximum threshold M.
 * @return The maximum threshold after one step transition.
 */
ll apply_f(ll M_val) {
    ll t_max = calculate_T_max(M_val);
    if (t_max == -1) {
        // If T_max is -infinity (no city reachable), the threshold doesn't increase.
        return M_val;
    }
    // The new threshold is the maximum of the current one and the T_max achievable.
    return max(M_val, t_max);
}

/**
 * @brief Computes f^(k)(M_val), i.e., applying the function f k times starting from M_val.
 *        Uses binary lifting (sparse table `jump`) for efficient computation.
 * @param M_val The initial maximum threshold.
 * @param k The number of steps (applications of f).
 * @return The maximum threshold after k steps.
 */
ll compute_f_k(ll M_val, int k) {
     // Basic validation: k must be non-negative.
     if (k < 0) throw runtime_error("k must be non-negative in compute_f_k"); 
     // If k=0, no steps are taken, return the initial value.
     if (k == 0) return M_val;

     // Determine the maximum power of 2 needed for binary lifting based on N
     int LOGN = N_global > 1 ? ceil(log2(N_global)) + 1 : 1; // Use ceil + 1 for safety
     LOGN = min(LOGN, LOGN_MAX); // Cap LOGN to avoid excessive memory/computation

     ll current_M = M_val;
     int current_idx = -1; // Index in the value map corresponding to current_M

     // Check if the initial M_val is one of the T values we track.
     auto it = value_to_idx.find(current_M);
     if (it == value_to_idx.end()) {
          // M_val is not in the map. This typically happens only if M_val = T_A and T_A is not in {T_1 .. T_N}.
          // Compute one step manually to potentially transition to a value within the map.
          current_M = apply_f(M_val);
          k--; // One step accounted for.
          if (k == 0) return current_M; // If only 1 step was needed.

          // Check if the new M is now in the map.
          it = value_to_idx.find(current_M);
          if (it == value_to_idx.end()) {
                // If it's still not in the map, this implies f(M_val) = M_val, meaning it stabilized immediately.
                // Our analysis shows f(T_A) = max(T_A, T_k). If result is T_A, it stabilized. If T_k, it's in map.
                // So this case means stabilization.
                return M_val; // Return the value it stabilized at (which is the original M_val).
          }
          // If it is in map now, record its index.
          current_idx = it->second;
     } else {
          // Initial M_val is in map. Record its index.
          current_idx = it->second;
     }

     // Proceed with binary lifting for the remaining k steps using the jump table.
     // Iterate through powers of 2 from largest down to smallest is standard, but 0 up works too.
     for (int p = 0; p < LOGN; ++p) { 
         // If the p-th bit of k is set, it means we need to take a jump of size 2^p.
         if ((k >> p) & 1) {
             current_idx = jump[p][current_idx];
         }
     }
     
     // After applying all necessary jumps, return the value corresponding to the final index.
     return idx_to_value[current_idx];
}

int main() {
    // Enable Fast IO
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    // Read number of cities
    cin >> N_global;
    int N = N_global; 

    vector<City> cities(N);
    vector<ll> T_vals_orig(N); // Store original T values to build map later
    // Read altitudes H_i
    for (int i = 0; i < N; ++i) {
        cities[i].id = i + 1; // Store 1-based ID
        cin >> cities[i].h;
    }
    // Read teleporter thresholds T_i
    for (int i = 0; i < N; ++i) {
        cin >> cities[i].t;
        T_vals_orig[i] = cities[i].t;
    }

    // Sort cities by altitude H to prepare for T_max calculation
    vector<City> sorted_cities = cities;
    sort(sorted_cities.begin(), sorted_cities.end(), compareCities);

    // Populate H_sorted and PMT (prefix maximum T) arrays
    H_sorted.resize(N);
    PMT.resize(N + 1); // Use 1-based indexing for PMT for convenience
    PMT[0] = -1LL; // Base case: max T for empty prefix is -infinity
    for (int i = 0; i < N; ++i) {
        H_sorted[i] = sorted_cities[i].h;
        // PMT[i+1] stores max T among first i+1 cities (indices 0 to i) sorted by H
        PMT[i + 1] = max(PMT[i], sorted_cities[i].t);
    }
    
    // Prepare value map (value_to_idx, idx_to_value) for T values
    // Collect distinct T values
    vector<ll> distinct_T = T_vals_orig;
    sort(distinct_T.begin(), distinct_T.end());
    distinct_T.erase(unique(distinct_T.begin(), distinct_T.end()), distinct_T.end());

    // Build the map and inverse map
    int distinct_count = 0;
    for (ll t_val : distinct_T) {
        value_to_idx[t_val] = distinct_count++;
        idx_to_value.push_back(t_val);
    }
    
    int V = distinct_count; // Number of distinct T values
    int LOGN = N > 1 ? ceil(log2(N)) + 1 : 1; // Calculate required LOGN based on N
    LOGN = min(LOGN, LOGN_MAX); // Cap LOGN to defined maximum

    // Initialize jump table
    jump.resize(LOGN, vector<int>(V));

    // Precompute jump table Base case: p=0 (jump of size 2^0 = 1)
    for (int i = 0; i < V; ++i) {
        ll current_val = idx_to_value[i];
        ll next_val = apply_f(current_val); // Apply f once
        
        auto it = value_to_idx.find(next_val);
        if (it != value_to_idx.end()) {
             // Store the index corresponding to the next value
             jump[0][i] = it->second;
        } else {
             // According to analysis, f(T_i) should always yield a value that is also a T_j (possibly T_i itself).
             // If next_val is not found in the map, it indicates an error in reasoning or implementation.
             throw runtime_error("f(T_i) result not found in map during precomputation");
        }
    }

    // Compute jump table for p > 0 using dynamic programming approach
    // jump[p][i] = jump[p-1][ jump[p-1][i] ] essentially jumps 2^(p-1) steps twice.
    for (int p = 1; p < LOGN; ++p) {
        for (int i = 0; i < V; ++i) {
            jump[p][i] = jump[p - 1][jump[p - 1][i]];
        }
    }

    // Read number of queries
    int Q;
    cin >> Q;
    // Process each query
    while (Q--) {
        int A_idx, B_idx; // Read 1-based city indices
        cin >> A_idx >> B_idx;
        int A = A_idx - 1; // Convert to 0-based index
        int B = B_idx - 1; 

        ll T_A = cities[A].t; // Teleporter threshold of starting city A
        ll H_B = cities[B].h; // Altitude of destination city B

        // Check base case: Can B be reached in 1 step from A?
        // Condition: H_B <= T_A (altitude of B is within A's teleporter threshold)
        if (H_B <= T_A) {
            cout << 1 << "\n";
            continue; // Process next query
        }

        // Binary search for the minimum number of steps k >= 1.
        // Search space for k is [1, N]. Max steps needed is N-1, but checking up to N is safe.
        int low = 1, high = N, ans = -1; // Initialize answer to -1 (unreachable)
        
        while(low <= high) {
            int mid_k = low + (high - low) / 2; // Candidate minimum steps

            // Check if B is reachable within mid_k steps.
            // The condition is H_B <= M_{mid_k - 1}, where M_{k} = f^k(T_A).
            // Compute M_{mid_k - 1} using binary lifting function.
            ll M_km1 = compute_f_k(T_A, mid_k - 1);
                        
            if (H_B <= M_km1) {
                // If H_B is within the threshold after mid_k - 1 steps, 
                // it means B is reachable in mid_k steps (or possibly earlier).
                ans = mid_k; // mid_k is a possible answer.
                high = mid_k - 1; // Try to find a smaller k.
            } else {
                // If H_B is greater than the threshold, mid_k steps are not enough.
                low = mid_k + 1; // Need more steps, minimum k must be > mid_k.
            }
        }
        // Output the minimum steps found, or -1 if unreachable.
        cout << ans << "\n";
    }

    return 0;
}
0