結果
問題 |
No.1631 Sorting Integers (Multiple of K) Easy
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:15:34 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 8,379 bytes |
コンパイル時間 | 1,234 ms |
コンパイル使用メモリ | 102,276 KB |
実行使用メモリ | 138,332 KB |
最終ジャッジ日時 | 2025-05-14 13:16:53 |
合計ジャッジ時間 | 7,312 ms |
ジャッジサーバーID (参考情報) |
judge5 / judge2 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 4 |
other | AC * 11 TLE * 5 -- * 12 |
ソースコード
#include <iostream> #include <vector> #include <numeric> #include <unordered_map> #include <utility> // For std::pair #include <functional> // For std::hash using namespace std; // Custom hash function for std::pair<long long, int> // This is necessary to use std::pair as a key in std::unordered_map. // Using a good hash combination technique helps reduce collisions and improve performance. struct pair_hash { template <class T1, class T2> std::size_t operator () (const std::pair<T1, T2>& p) const { auto h1 = std::hash<T1>{}(p.first); auto h2 = std::hash<T2>{}(p.second); // Combine the hashes. A common method is inspired by boost::hash_combine. // This approach helps in distributing hash values better and potentially reducing collisions. size_t seed = 0; // Combine h1 into seed seed ^= h1 + 0x9e3779b9 + (seed << 6) + (seed >> 2); // Combine h2 into seed seed ^= h2 + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed; } }; // Encodes the counts vector (c_1, ..., c_9) into a single long long integer. // The problem constraints state N <= 14. This means each digit count c_i will also be <= 14. // Since 14 < 16 = 2^4, each count can be represented using 4 bits. // There are 9 digits (1 through 9), so the total number of bits needed is 9 counts * 4 bits/count = 36 bits. // This fits comfortably within a 64-bit long long integer type. long long encode_counts(const vector<int>& counts) { long long code = 0; for (int i = 0; i < 9; ++i) { // Shift the current code left by 4 bits to make space for the next count. // Then, bitwise OR the count into the lowest 4 bits. code = (code << 4) | counts[i]; } return code; } // DP state storage: An array of unordered_maps. // dp[k] will store the dynamic programming states for prefixes of length k. // The key of the map is a std::pair<long long, int> representing (encoded_counts, remainder). // The value associated with the key is a long long storing the number of ways (distinct permutations) to reach that state. // The array size is 15 to accommodate N up to 14 (using indices 0 through 14). unordered_map<pair<long long, int>, long long, pair_hash> dp[15]; // Stores the initial counts c_1 through c_9 provided in the input. vector<int> initial_counts(9); int N; // Total number of digits. int K; // The divisor K. int main() { // Enable fast I/O operations for potentially large inputs/outputs. ios_base::sync_with_stdio(false); cin.tie(NULL); // Read N (total number of digits) and K (the divisor) from standard input. cin >> N >> K; // Read the initial counts for digits 1 through 9. for (int i = 0; i < 9; ++i) { cin >> initial_counts[i]; } // Initialize the DP table with the base case. // A prefix of length 0 uses zero counts for all digits. // The value of an empty prefix is considered 0. The remainder modulo K is 0. // There is exactly one way to form an empty prefix (the empty sequence). vector<int> zero_counts(9, 0); long long initial_code = encode_counts(zero_counts); dp[0][{initial_code, 0}] = 1; // Perform the dynamic programming state transitions level by level. // Iterate through prefix lengths k from 0 up to N-1. for (int k = 0; k < N; ++k) { // Optimization: If dp[k] is empty, it means no valid prefixes of length k could be formed. // This implies no prefixes of length k+1 or greater can be formed either. We can stop the process early. if (dp[k].empty()) { break; } // Iterate through all states reachable at the current level k. // Each entry in the map dp[k] is a pair: {state_key, count_value}. // 'state' is the key: pair<long long, int> = (encoded_counts, remainder). // 'count' is the value: long long = number of ways to reach this state. for (auto const& [state, count] : dp[k]) { // If the count for a state is 0, it means this state is practically unreachable (or already processed leading to zero paths). Skip it. if (count == 0) continue; long long current_code = state.first; int current_rem = state.second; // Decode the 'current_code' back into a vector of counts. // This is necessary to check the availability of each digit 'd' for appending to the prefix. vector<int> current_counts(9); long long temp_code = current_code; // Decode the counts from the encoded long long. The counts are stored from most significant bits (digit 9) to least significant (digit 1). // We decode from right to left (index 8 down to 0) to extract counts correctly. for (int i = 8; i >= 0; --i) { current_counts[i] = temp_code & 0xF; // Extract the count for digit i+1 using bitwise AND with mask 1111 (0xF). temp_code >>= 4; // Shift right by 4 bits to process the next count stored in higher bits. } // Try appending each possible digit 'd' (from 1 to 9). for (int d = 1; d <= 9; ++d) { // Check if digit 'd' is available in the multiset. // This is true if the count of 'd' used so far (current_counts[d-1]) // is less than the total count of 'd' available initially (initial_counts[d-1]). if (current_counts[d - 1] < initial_counts[d - 1]) { // If digit 'd' is available: // Compute the counts vector for the new prefix (length k+1). vector<int> next_counts = current_counts; next_counts[d - 1]++; // Increment the count for the digit 'd' that was just used. long long next_code = encode_counts(next_counts); // Encode this new counts vector into a long long key. // Compute the remainder of the new prefix value modulo K. // The new prefix value is conceptually (old_prefix_value * 10 + d). // By modular arithmetic properties: new_remainder = (old_remainder * 10 + d) % K. // Perform the calculation using 'long long' for the intermediate multiplication `(long long)current_rem * 10` // to prevent potential integer overflow, although with K <= 1000 it might be safe for `int`. Using `long long` is safer. int next_rem = (int)(((long long)current_rem * 10 + d) % K); // Update the DP table for level k+1. // Add the number of ways ('count') to reach the current state // to the total ways count for the 'next_state'. This accumulates counts from different paths leading to the same state. dp[k + 1][{next_code, next_rem}] += count; } } } // Optional memory optimization: If memory usage becomes a concern, one could potentially clear dp[k] // after it's fully processed, as it won't be needed for subsequent levels k+2 onwards. // Example: dp[k].clear(); dp[k].rehash(0); // However, this might incur performance overhead due to map reallocations. Given the 512MB limit, it's likely unnecessary for N=14. } // After the loops complete, the final answer is stored in dp[N]. // We are interested in the count for the state that represents using exactly the initial multiset of digits // (encoded by 'final_code') and results in a remainder of 0 modulo K. long long final_code = encode_counts(initial_counts); // Check if the target state {final_code, 0} exists in the map dp[N]. // Using .count() is a safe way to check existence without potentially inserting a new element if the key is not found (unlike operator[]). if (dp[N].count({final_code, 0})) { // If the state exists, its associated value is the answer. Print it. cout << dp[N][{final_code, 0}] << endl; } else { // If the state {final_code, 0} was never reached, it means no permutation // of the given digits forms a number divisible by K. The count is 0. cout << 0 << endl; } return 0; }