結果
問題 |
No.2039 Copy and Avoid
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:13:21 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
AC
|
実行時間 | 69 ms / 2,000 ms |
コード長 | 9,538 bytes |
コンパイル時間 | 1,398 ms |
コンパイル使用メモリ | 115,556 KB |
実行使用メモリ | 7,844 KB |
最終ジャッジ日時 | 2025-05-14 13:14:12 |
合計ジャッジ時間 | 2,939 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge3 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 31 |
ソースコード
#include <iostream> #include <vector> #include <map> #include <set> #include <algorithm> #include <cmath> // Include cmath for sqrt using namespace std; // Define long long type for large integers typedef long long ll; // Use a sufficiently large value for infinity. // Maximum possible cost could be around N*A, which can be up to 10^9 * 10^9 = 10^18. // std::numeric_limits<ll>::max() is about 9e18. 4e18 should be safe enough as infinity marker. const ll INF = 4e18; int main() { // Faster I/O operations ios_base::sync_with_stdio(false); cin.tie(NULL); ll N; // Target value for x int M; // Number of forbidden integers ll A, B; // Costs for operation a and operation b cin >> N >> M >> A >> B; set<ll> forbidden_set; // Use set for efficient lookup if needed, though not primarily used in the final DP approach vector<ll> C_input(M); // Store input forbidden integers for (int i = 0; i < M; ++i) { cin >> C_input[i]; forbidden_set.insert(C_input[i]); } // Sort the forbidden integers. This helps in efficiently finding the first forbidden multiple later. sort(C_input.begin(), C_input.end()); // Base case: If N=1, we are already at the target state (1, 1). Cost is 0. if (N == 1) { cout << 0 << endl; return 0; } // Problem constraints state C_i >= 2 and C_i <= N-1. // This implies that the initial state x=1 is never forbidden, and the target state x=N is never forbidden. // Find all divisors of N. This is needed because the DP state relies on divisors. vector<ll> divs; ll sqrtN = sqrt(N); // Compute square root once for efficiency for (ll i = 1; i <= sqrtN; ++i) { if (N % i == 0) { divs.push_back(i); // Add the corresponding larger divisor if it's different from i if (N / i != i) { divs.push_back(N / i); } } } sort(divs.begin(), divs.end()); // Sort the divisors int tauN = divs.size(); // Number of divisors of N map<ll, int> div_idx; // Map divisor value to its index in the sorted vector `divs` for (int i = 0; i < tauN; ++i) { div_idx[divs[i]] = i; } // Precompute the minimum forbidden multiple for each divisor d'. // This optimization speeds up validity checks during DP transitions. // `min_forbidden_multiple[i]` stores the minimum forbidden value C_p such that C_p > divs[i] and divs[i] divides C_p. // If no such forbidden multiple exists, it stores INF. vector<ll> min_forbidden_multiple(tauN, INF); for (int i = 0; i < tauN; ++i) { ll d_prime = divs[i]; // Iterate through the sorted list of forbidden values for (ll forbidden_val : C_input) { // Check conditions: must be greater than d' and divisible by d' if (forbidden_val > d_prime && forbidden_val % d_prime == 0) { min_forbidden_multiple[i] = forbidden_val; // First one found is the minimum because C_input is sorted break; // Found the minimum, no need to check further forbidden values for this d' } } } // DP table: dp[i] stores the minimum cost to reach the state (divs[i], divs[i]) vector<ll> dp(tauN, INF); // Initialize DP: The initial state is (1, 1) with cost 0. divs[0] is always 1. dp[0] = 0; // Dynamic Programming calculation // Iterate through all divisors d' = divs[i] for (int i = 0; i < tauN; ++i) { ll d_prime = divs[i]; // If state (d', d') is unreachable (cost is INF), skip transitions from it if (dp[i] == INF) continue; // Iterate through all larger divisors d = divs[j] for (int j = i + 1; j < tauN; ++j) { ll d = divs[j]; // Check if d is reachable from d' via a block operation: d = (1+k) * d' if (d % d_prime == 0) { // Calculate k: the number of 'a' operations in the block ll k = d / d_prime - 1; // Since j > i, d > d', so d/d' >= 2 (integer division if d' divides d). Thus k >= 1. // Check validity: The sequence of x values generated by 'a' operations is (1+1)d', (1+2)d', ..., (1+k)d' = d. // None of these intermediate values should be forbidden. // We only need to check the minimum forbidden multiple of d' that is > d'. bool valid = true; // Use the precomputed minimum forbidden multiple for d' (`divs[i]`) if (min_forbidden_multiple[i] <= d) { // If the minimum forbidden multiple is less than or equal to d, it means it's encountered during the 'a' operations sequence. valid = false; } if (valid) { // If the path segment is valid, update the DP state for d // Calculate cost for 'a' operations: k*A. Check for potential overflow. ll cost_A = 0; if (A > 0 && k > 0) { // Use long double for intermediate check to prevent overflow with standard `ll` multiplication if k and A are large // Check if k*A potentially exceeds INF. If so, treat cost as INF. if (k > INF / A) { // More robust check against overflow cost_A = INF; } else { cost_A = k * A; } } // Check for overflow before adding costs: dp[i] + cost_A + B if (cost_A == INF || dp[i] == INF || cost_A > INF - B || dp[i] > INF - B - cost_A) { // If calculation results in INF or would overflow, skip update (or assign INF explicitly) // Here we just continue, effectively skipping updates that would lead to costs >= INF } else { // Update dp[j] with the minimum cost found so far dp[j] = min(dp[j], dp[i] + cost_A + B); } } } } } // Calculate the final minimum cost to reach x = N ll min_total_cost = INF; // Consider the special path using only 'a' operations: (1, 1) -> (2, 1) -> ... -> (N, 1) // This path consists of N-1 'a' operations. Cost is (N-1)*A. bool special_path_valid = true; // This path is valid if and only if no forbidden number C_p exists in the range [2, N]. if (!C_input.empty()) { ll min_C = C_input[0]; // C_input is sorted, so C_input[0] is the minimum forbidden value. if (min_C <= N) { // If the smallest forbidden number is <= N, it blocks this path. special_path_valid = false; } } if (special_path_valid) { if (N > 1) { // Avoid calculation if N=1 (handled at start) ll cost_A = 0; if (A > 0 && N - 1 > 0) { if (N - 1 > INF / A) { // Check for overflow cost_A = INF; } else { cost_A = (N - 1) * A; } } min_total_cost = min(min_total_cost, cost_A); } else { min_total_cost = 0; // Should have been handled, but safe check. } } // Consider paths ending with a sequence of 'a' operations starting from a state (d, d) for (int i = 0; i < tauN; ++i) { ll d = divs[i]; // Current divisor representing state (d, d) // If state (d, d) is unreachable, skip if (dp[i] == INF) continue; // Check if N is reachable from d by multiplication factor (N = (1+k)d) if (N % d == 0) { // Calculate k: number of 'a' operations in the final sequence ll k = N / d - 1; // If k<0, this means d > N, impossible. N=d case is k=0. // Check validity: intermediate x values (1+p)d for 1 <= p <= k must not be forbidden bool valid = true; // Use the precomputed minimum forbidden multiple of d (`divs[i]`) that is > d if (min_forbidden_multiple[i] <= N) { // Check if this minimum forbidden multiple is encountered before or at N valid = false; } if (valid) { // Calculate cost for final 'a' operations ll cost_A = 0; if (A > 0 && k > 0) { if (k > INF / A) { // Check for overflow cost_A = INF; } else { cost_A = k * A; } } // Check for overflow before adding dp[i] + cost_A if (cost_A == INF || dp[i] == INF || cost_A > INF - dp[i]) { // If cost is INF or would overflow, skip this path } else { // Update the overall minimum total cost min_total_cost = min(min_total_cost, dp[i] + cost_A); } } } } // Output the final result if (min_total_cost == INF) { cout << -1 << endl; // If INF, it's impossible to reach N } else { cout << min_total_cost << endl; // Otherwise output the minimum cost found } return 0; }