結果

問題 No.1330 Multiply or Divide
ユーザー qwewe
提出日時 2025-05-14 12:59:19
言語 C++17
(gcc 13.3.0 + boost 1.87.0)
結果
WA  
実行時間 -
コード長 8,351 bytes
コンパイル時間 951 ms
コンパイル使用メモリ 95,252 KB
実行使用メモリ 7,844 KB
最終ジャッジ日時 2025-05-14 13:00:28
合計ジャッジ時間 5,926 ms
ジャッジサーバーID
(参考情報)
judge3 / judge5
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other WA * 1 TLE * 1 -- * 44
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <iostream>
#include <vector>
#include <queue>
#include <map>
#include <utility>
#include <algorithm>

// Function to calculate v_P(n), the exponent of prime P in the factorization of n.
// v_P(n) counts how many times n is divisible by P.
long long vp(long long n, long long p) {
    // Base cases and edge cases
    if (n == 0) return 1e18; // A large number to represent practically infinite factors for 0
    if (p <= 1) return 0; // P must be a prime > 1. Division by 1 is trivial.
    
    long long count = 0;
    // Keep dividing n by p as long as it's divisible
    while (n > 0 && n % p == 0) {
        count++;
        n /= p;
    }
    return count;
}

// Use a large value for infinity for costs. 
// The maximum possible cost is likely bounded, but use a sufficiently large value.
// Given constraints, paths are unlikely to exceed cost that fits in long long.
// 4e18 is close to max long long / 2, safe for additions.
const long long INF = 4e18; 

int main() {
    // Use faster I/O operations
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);
    
    int N; // Number of elements in sequence A
    long long M; // Threshold value
    long long P; // Prime number P
    std::cin >> N >> M >> P;
    
    // Use a map to store the minimum cost C_k required to achieve multiplication by B_k.
    // B_k is defined as A_i / P^{v_P(A_i)}.
    // Key: B_k value, Value: minimum cost C_k = 1 + v_P(A_i).
    std::map<long long, long long> min_cost_for_B;

    for (int i = 0; i < N; ++i) {
        long long current_A;
        std::cin >> current_A;
        // Problem constraints state A_i >= 1. Skip if non-positive for robustness.
        if (current_A <= 0) continue; 
        
        // Calculate exponent of P in current_A's factorization
        long long e_i = vp(current_A, P);
        // Calculate B_i = A_i / P^{e_i} using repeated division to avoid large powers/floating point issues.
        long long B_i = current_A;
        for(long long k = 0; k < e_i; ++k) {
             // This check should not be necessary if A_i >= 1 and P >= 2.
             if (B_i == 0) break; 
             B_i /= P;
        }

        // The total cost for choosing A_i is 1 (for multiplication) + e_i (for divisions by P).
        long long cost = 1 + e_i;
        
        // Check if this B_i value is already in the map.
        if (min_cost_for_B.count(B_i)) {
            // If yes, update the cost if the current A_i provides a cheaper way to get this B_i.
            min_cost_for_B[B_i] = std::min(min_cost_for_B[B_i], cost);
        } else {
            // If no, insert this new B_i and its associated cost into the map.
            min_cost_for_B[B_i] = cost;
        }
    }
    
    // Store the unique (B_k, C_k) pairs from the map into a vector for efficient iteration during Dijkstra.
    std::vector<std::pair<long long, long long>> transitions;
    for (auto const& [b_val, cost_val] : min_cost_for_B) {
        transitions.push_back({b_val, cost_val});
    }

    // Priority queue for Dijkstra algorithm. Stores {cost, state} pairs.
    // Uses std::greater to make it a min-priority queue based on cost.
    std::priority_queue<std::pair<long long, long long>, std::vector<std::pair<long long, long long>>, std::greater<std::pair<long long, long long>>> pq;
    
    // Map to store the minimum distance (cost) found so far to reach each state `x`.
    std::map<long long, long long> dist;
    
    // Initial state: x=1, cost=0.
    // Check if the initial state 1 already exceeds M. M >= 1, so this is not possible.
    // The problem implies operations continue *while* x <= M.
    
    // Set distance to initial state 1 as 0 and push it to the priority queue.
    dist[1] = 0;
    pq.push({0, 1});
    
    // Variable to store the minimum total cost found to reach any state > M. Initialize to infinity.
    long long min_total_cost = INF;
    
    // Dijkstra algorithm main loop
    while (!pq.empty()) {
        // Extract the state `u` with the minimum cost `current_cost` from the priority queue.
        auto [current_cost, u] = pq.top();
        pq.pop();
        
        // Check if we have already found a shorter path to `u`.
        // Need to use `dist.count(u)` because map access `dist[u]` creates entry with value 0 if key `u` doesn't exist.
        // `dist.count(u) == 0` means distance is effectively infinity.
        // If `current_cost > dist[u]`, this queue entry is outdated, skip it.
        if (dist.count(u) == 0 || current_cost > dist[u]) {
             continue;
        }

        // Optimization: If the current path's cost is already not better than the best cost found so far
        // to reach the goal region (x > M), then exploring further from this state `u` cannot lead to
        // a better overall solution. Prune this path.
        if (current_cost >= min_total_cost) {
            continue;
        }

        // Iterate through all possible transitions defined by the unique (B_k, C_k) pairs.
        for (const auto& transition : transitions) {
            long long B_k = transition.first;
            long long C_k = transition.second;

            // Since A_i >= 1, B_k = A_i / P^{e_i} must be >= 1. Safety check.
             if (B_k <= 0) continue; 

            // Check if multiplying state `u` by `B_k` will result in a value exceeding `M`.
            bool exceeds_M = false;
            // To avoid overflow when calculating u * B_k, use property of integer division:
            // For positive integers u, B_k, M: u * B_k > M  is equivalent to u > M / B_k.
            // This check works correctly even if u * B_k would overflow standard 64-bit integer types.
            // Need to handle B_k = 0? No, B_k >= 1 guaranteed.
            
            // If B_k > M, then for u >= 1, u * B_k will always be > M.
            if (B_k > M && u >= 1) { 
                exceeds_M = true;
            } 
            // Check only if B_k <= M and u > 0.
            // If u = 0, result is 0, which is <= M. But u starts at 1 and is multiplied by B_k >= 1, so u >= 1 always.
            else if (u > 0 && B_k > 0) { 
                 // Calculate M / B_k using integer division.
                 long long M_div_Bk = M / B_k;
                 // If u > M / B_k, then u * B_k > M.
                 if (u > M_div_Bk) {
                     exceeds_M = true;
                 }
            }
           
            if (exceeds_M) {
                // If the resulting value v = u * B_k exceeds M, this path reaches the goal region.
                // Update the overall minimum cost found so far if this path is cheaper.
                min_total_cost = std::min(min_total_cost, current_cost + C_k);
            } else {
                // If the resulting value v = u * B_k does not exceed M.
                // Calculate the next state value `v` and the total cost `next_cost` to reach it.
                long long v = u * B_k; // This multiplication is safe if check passed, v <= M potentially.
                long long next_cost = current_cost + C_k;
                
                // Check if this path to `v` is shorter than any previously found path to `v`.
                // `!dist.count(v)` means `v` has not been reached before (distance is effectively INF).
                if (!dist.count(v) || next_cost < dist[v]) {
                     // Optimization: Only explore this path if its cost `next_cost` is potentially better
                     // than the current best cost `min_total_cost` found to reach the goal region.
                     if (next_cost < min_total_cost) { 
                         // Update the minimum distance to `v` and add/update `v` in the priority queue.
                         dist[v] = next_cost;
                         pq.push({next_cost, v});
                     }
                }
            }
        }
    }
    
    // After Dijkstra algorithm finishes:
    // If `min_total_cost` still holds the initial INF value, it means no path could reach a state > M.
    // This corresponds to the condition where the process might repeat infinitely without exceeding M.
    if (min_total_cost == INF) {
        std::cout << -1 << std::endl;
    } else {
        // Otherwise, output the minimum total cost found.
        std::cout << min_total_cost << std::endl;
    }
    
    return 0;
}
0