結果
| 問題 |
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 |
ソースコード
#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;
}
qwewe