結果
問題 | No.2242 Cities and Teleporters |
ユーザー |
![]() |
提出日時 | 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 |
ソースコード
#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; }