Submission #1239364

#TimeUsernameProblemLanguageResultExecution timeMemory
1239364orcslopOsmosmjerka (COCI17_osmosmjerka)C++20
160 / 160
3214 ms37428 KiB
#include <iostream> #include <vector> #include <string> #include <numeric> #include <map> #include <algorithm> // Use long long for hash values and counts to avoid overflow. using ll = long long; // Constants for polynomial rolling hash. Using two different pairs of // prime bases and moduli helps prevent hash collisions. const ll P1 = 31, M1 = 1e9 + 7; const ll P2 = 37, M2 = 1e9 + 9; // Global vectors to store precomputed powers of the bases P1 and P2. std::vector<ll> p_pow1, p_pow2; // Grid dimensions, word length K, and the grid itself. int R, C; ll K; std::vector<std::string> grid; // --- Utility Functions --- // Helper for modular addition. ll add(ll a, ll b, ll m) { return (a + b) % m; } // Helper for modular multiplication. ll mul(ll a, ll b, ll m) { return (a * b) % m; } // Helper for modular subtraction that handles negative results correctly. ll sub(ll a, ll b, ll m) { return (a - b % m + m) % m; } // Standard library functions for GCD and LCM. ll gcd(ll a, ll b) { return std::gcd(a, b); } ll lcm(ll a, ll b) { if (a == 0 || b == 0) return 0; // To prevent overflow, divide before multiplying. return (a / gcd(a, b)) * b; } /** * @brief Retrieves the character from the infinitely repeating grid. * @param r The target row. * @param c The target column. * @return The character at the specified coordinates. */ char get_char(ll r, ll c) { // The modulo operator in C++ can return negative results. // Adding the dimension before the second modulo ensures the result is always positive. ll final_r = (r % R + R) % R; ll final_c = (c % C + C) % C; return grid[final_r][final_c]; } /** * @brief Precomputes the powers of the hash bases up to a maximum length. * @param max_len The maximum length for which to compute powers. */ void precompute_powers(ll max_len) { p_pow1.resize(max_len + 1); p_pow2.resize(max_len + 1); p_pow1[0] = p_pow2[0] = 1; for (int i = 1; i <= max_len; ++i) { p_pow1[i] = mul(p_pow1[i - 1], P1, M1); p_pow2[i] = mul(p_pow2[i - 1], P2, M2); } } // --- Main Logic --- void solve() { // Read input dimensions and the grid pattern. std::cin >> R >> C >> K; grid.resize(R); for (int i = 0; i < R; ++i) { std::cin >> grid[i]; } // Any word is a periodic sequence. The maximum possible period is lcm(R, C). // Words longer than this length don't help distinguish unique sequences. // So, we can cap K at lcm(R, C). ll eff_K = K; ll max_period = lcm(R, C); if (max_period > 0) { eff_K = std::min(K, max_period); } // If effective length is 0, all words are empty and thus identical. if (eff_K == 0) { std::cout << "1/1\n"; return; } precompute_powers(eff_K); // Map to store counts of each unique hash pair. std::map<std::pair<ll, ll>, ll> hash_counts; // Define the 8 directions of travel (row_change, col_change). int dr[] = {-1, -1, -1, 0, 0, 1, 1, 1}; int dc[] = {-1, 0, 1, -1, 1, -1, 0, 1}; // Iterate through each of the 8 directions. for (int d = 0; d < 8; ++d) { int current_dr = dr[d]; int current_dc = dc[d]; // This hash table stores computed hashes for the current direction. std::vector<std::vector<std::pair<ll, ll>>> dir_hashes(R, std::vector<std::pair<ll, ll>>(C)); // Determine the grid traversal order. To use the rolling hash update, // we must compute the hash for a cell (r,c) AFTER its predecessor // (r-dr, c-dc) has been computed. This loop setup ensures that. int r_start = (current_dr >= 0) ? 0 : R - 1; int r_end = (current_dr >= 0) ? R : -1; int r_step = (current_dr >= 0) ? 1 : -1; int c_start = (current_dc >= 0) ? 0 : C - 1; int c_end = (current_dc >= 0) ? C : -1; int c_step = (current_dc >= 0) ? 1 : -1; for (int r = r_start; r != r_end; r += r_step) { for (int c = c_start; c != c_end; c += c_step) { // A cell is on the "perimeter" for this direction if it's in the // first row or column of our traversal. For these cells, we can't // roll from a previous hash, so we compute it from scratch. bool is_perimeter = (r == r_start) || (c == c_start); ll h1, h2; if (is_perimeter) { // Full O(eff_K) hash calculation for perimeter cells. h1 = 0; h2 = 0; for (int k = 0; k < eff_K; ++k) { char ch = get_char(r + (ll)k * current_dr, c + (ll)k * current_dc); h1 = add(mul(h1, P1, M1), ch - 'a' + 1, M1); h2 = add(mul(h2, P2, M2), ch - 'a' + 1, M2); } } else { // O(1) rolling hash update for interior cells. int prev_r = (r - current_dr + R) % R; int prev_c = (c - current_dc + C) % C; ll prev_h1 = dir_hashes[prev_r][prev_c].first; ll prev_h2 = dir_hashes[prev_r][prev_c].second; char first_char_of_prev = get_char(prev_r, prev_c); char last_char_of_curr = get_char(r + (ll)(eff_K - 1) * current_dr, c + (ll)(eff_K - 1) * current_dc); // The rolling hash formula: H_new = (H_old - val(first) * P^(K-1)) * P + val(last) ll term1_h1 = mul(first_char_of_prev - 'a' + 1, p_pow1[eff_K - 1], M1); h1 = sub(prev_h1, term1_h1, M1); h1 = mul(h1, P1, M1); h1 = add(h1, last_char_of_curr - 'a' + 1, M1); ll term1_h2 = mul(first_char_of_prev - 'a' + 1, p_pow2[eff_K - 1], M2); h2 = sub(prev_h2, term1_h2, M2); h2 = mul(h2, P2, M2); h2 = add(h2, last_char_of_curr - 'a' + 1, M2); } // Store the computed hash and increment its count in the global map. dir_hashes[r][c] = {h1, h2}; hash_counts[{h1, h2}]++; } } } // Calculate the final probability. // The probability of two independent events (picking the same word) is the sum // of the squares of the individual probabilities for each unique word. // P(same) = sum over all unique words w [ (count(w)/total)^2 ] // This simplifies to (sum of count(w)^2) / total^2 ll total_strings = (ll)R * C * 8; ll numerator = 0; for (auto const& [key, val] : hash_counts) { numerator += val * val; } ll denominator = total_strings * total_strings; // Simplify the fraction by dividing by the greatest common divisor. ll common_divisor = gcd(numerator, denominator); std::cout << numerator / common_divisor << "/" << denominator / common_divisor << "\n"; } int main() { // Fast I/O std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); solve(); return 0; }
#Verdict Execution timeMemoryGrader output
Fetching results...