結果
問題 |
No.603 hel__world (2)
|
ユーザー |
![]() |
提出日時 | 2025-05-14 13:02:29 |
言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
結果 |
TLE
|
実行時間 | - |
コード長 | 13,041 bytes |
コンパイル時間 | 1,347 ms |
コンパイル使用メモリ | 103,084 KB |
実行使用メモリ | 91,364 KB |
最終ジャッジ日時 | 2025-05-14 13:04:17 |
合計ジャッジ時間 | 7,042 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge4 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
sample | AC * 3 |
other | AC * 11 TLE * 1 -- * 18 |
ソースコード
#include <iostream> #include <vector> #include <string> #include <numeric> #include <map> #include <queue> #include <algorithm> // Required for std::min/max #include <string> // Required for std::reverse // Using __int128_t for large integer arithmetic if available. // This type is typically available in GCC/Clang compilers. #ifdef __GNUC__ typedef __int128_t u128; #else // Fallback or error if __int128_t is not available #warning "__int128_t not supported, using long long. Large values might overflow." typedef long long u128; // Note: This fallback is likely insufficient for the constraints. #endif // Function to print __int128_t (useful for debugging) #ifdef __GNUC__ std::ostream& operator<<(std::ostream& os, u128 val) { if (val < 0) { os << "-"; val = -val; } if (val == 0) { return os << "0"; } std::string s = ""; while (val > 0) { s += (char)('0' + (val % 10)); val /= 10; } std::reverse(s.begin(), s.end()); return os << s; } #endif // Array to store maximum counts for each character 'a' through 'z'. long long S_counts[26]; // The target subsequence string T. std::string T; // The modulus N = 10^6 + 3. const int MOD = 1e6 + 3; // Modular Arithmetic structure (Modint) struct Modint { long long val; Modint(long long v = 0) { // Ensure value is non-negative before taking modulo if (v < 0) v = v % MOD + MOD; val = v % MOD; } // Normalize value to be within [0, MOD-1] void normalize() { if (val < 0) val = val % MOD + MOD; val %= MOD; } // Overloaded operators for modular arithmetic Modint& operator+=(const Modint& other) { val += other.val; if (val >= MOD) val -= MOD; return *this; } Modint& operator-=(const Modint& other) { val -= other.val; if (val < 0) val += MOD; return *this; } Modint& operator*=(const Modint& other) { val = (val * other.val) % MOD; return *this; } // Modular exponentiation (for division via modular inverse) static Modint power(Modint base, long long exp) { Modint res = 1; base.normalize(); while (exp > 0) { if (exp % 2 == 1) res *= base; base *= base; exp /= 2; } return res; } // Modular inverse using Fermat's Little Theorem (since MOD is prime) Modint inv() const { return power(*this, MOD - 2); } Modint& operator/=(const Modint& other) { *this *= other.inv(); return *this; } // Friend operators for convenience friend Modint operator+(Modint a, const Modint& b) { return a += b; } friend Modint operator-(Modint a, const Modint& b) { return a -= b; } friend Modint operator*(Modint a, const Modint& b) { return a *= b; } friend Modint operator/(Modint a, const Modint& b) { return a /= b; } }; // Precomputed factorials and inverse factorials for combinations calculation const int MAX_N_MOD = 1e6 + 3; Modint fact[MAX_N_MOD]; Modint invFact[MAX_N_MOD]; // Precomputes factorials and inverse factorials modulo MOD up to MAX_N_MOD - 1. void precompute_combinations() { fact[0] = 1; for (int i = 1; i < MAX_N_MOD; ++i) { fact[i] = fact[i - 1] * Modint(i); } // Compute inverse factorial using modular inverse of the largest factorial invFact[MAX_N_MOD - 1] = fact[MAX_N_MOD - 1].inv(); for (int i = MAX_N_MOD - 2; i >= 0; --i) { invFact[i] = invFact[i+1] * Modint(i+1); } } // Computes combinations C(n, r) modulo MOD using precomputed values. // Uses Lucas' Theorem property implicitly: C(n, r) % MOD = C(n % MOD, r % MOD) % MOD. Modint nCr_mod(long long n, long long r) { // Basic validity checks if (r < 0 || r > n) return Modint(0); // Apply property based on Lucas' Theorem long long n_mod = n % MOD; // Since |T| <= 10^6 and MOD = 10^6+3, r corresponds to l_j which is <= |T| < MOD. // So r % MOD = r. // If r > n % MOD, then C(n % MOD, r) = 0. if (r > n_mod) return Modint(0); // Calculate C(n_mod, r) using factorials return fact[n_mod] * invFact[r] * invFact[n_mod - r]; } // Structure to represent ratios for priority queue comparisons. // Uses __int128_t to handle potentially large numerators and denominators. struct Ratio { u128 num, den; // Constructor initializes ratio, ensures positive denominator. Ratio(u128 n = 0, u128 d = 1) : num(n), den(d) { // Denominator should always be positive based on problem logic (k >= l >= 1 implies k+1-l >= 1). // Add safety check and normalization just in case. if (den == 0) { den = 1; // Avoid division by zero issues. } else if (den < 0) { num = -num; den = -den; } } // Comparison operator for priority queue ordering (max heap). // Compares A/B < C/D as A*D < C*B to avoid floating point. bool operator<(const Ratio& other) const { return num * other.den < other.num * den; } // Other comparison operators (useful for checks) bool operator>(const Ratio& other) const { return other < *this; } bool operator>=(const Ratio& other) const { return !(*this < other); } bool operator==(const Ratio& other) const { return num * other.den == other.num * den; } }; int main() { // Faster I/O std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); // Read maximum counts for each character for (int i = 0; i < 26; ++i) { std::cin >> S_counts[i]; } // Read target subsequence string T std::cin >> T; // Handle empty T case (problem states |T| >= 1, but good practice) if (T.empty()) { std::cout << 1 << std::endl; return 0; } // Compute T' (T after collapsing consecutive chars) and corresponding block lengths l_j. std::string T_prime = ""; std::vector<long long> l_values; if (!T.empty()) { T_prime += T[0]; l_values.push_back(1); for (size_t i = 1; i < T.length(); ++i) { if (T[i] == T[i - 1]) { l_values.back()++; } else { T_prime += T[i]; l_values.push_back(1); } } } int p = T_prime.length(); // Number of blocks in T' // If T resulted in empty T', handle appropriately (e.g., T itself was empty) if (p == 0) { std::cout << 1 << std::endl; // Assuming convention for empty string T return 0; } // Initialize k_j values (block lengths in S) to minimum required l_j. std::vector<long long> k_values = l_values; // Calculate total required count L_alpha for each character based on T. std::map<char, long long> L_alpha; for(int j=0; j<p; ++j) { L_alpha[T_prime[j]] += l_values[j]; } // Calculate remaining budget R_alpha for each character and total remaining budget R_total. std::vector<long long> R_alpha(26); // Use long long for budgets u128 R_total_128 = 0; // Use 128-bit integer for total budget bool possible = true; for (char alpha = 'a'; alpha <= 'z'; ++alpha) { long long required = L_alpha.count(alpha) ? L_alpha[alpha] : 0; // Check if available count S_counts is sufficient if (S_counts[alpha - 'a'] < required) { possible = false; break; } // Calculate remaining budget R_alpha[alpha - 'a'] = S_counts[alpha - 'a'] - required; // Add to total budget only if positive if (R_alpha[alpha - 'a'] > 0) R_total_128 += (u128)R_alpha[alpha - 'a']; } // If constraints cannot be met, output 0. if (!possible) { std::cout << 0 << std::endl; return 0; } // Precompute factorials and inverse factorials for nCr calculations. precompute_combinations(); // Priority queue to manage blocks based on their current ratio (potential gain). // Stores pairs of (Ratio, block_index). Max heap based on Ratio comparison. std::priority_queue<std::pair<Ratio, int>> pq; for (int j = 0; j < p; ++j) { long long L = l_values[j]; // Initial ratio is (L+1)/1 because k_j starts at L. Ratio r = { (u128)L + 1, 1 }; pq.push({r, j}); } // Greedy distribution of remaining budget using batch updates while (R_total_128 > 0 && !pq.empty()) { std::pair<Ratio, int> top = pq.top(); pq.pop(); int j1 = top.second; // Index of the block with highest ratio char c = T_prime[j1]; // Character of this block int alpha_idx = c - 'a'; // Index for budget array // Skip if budget for this character is already zero if (R_alpha[alpha_idx] == 0) continue; Ratio ratio1 = top.first; // The ratio of the current top block Ratio ratio2 = {1, 1}; // Default minimum ratio is 1/1 (effectively lowest possible gain) // Peek at the next element's ratio to determine batch size if (!pq.empty()) { ratio2 = pq.top().first; } long long K = k_values[j1]; // Current length k_j1 long long L = l_values[j1]; // Required length l_j1 (constant) u128 current_K_128 = K; // Use 128-bit for calculations // Binary search to find the maximum number of increments `max_x` such that // the ratio of block j1 after `max_x` increments is still >= ratio2. u128 low = 0, high = R_alpha[alpha_idx], max_x = 0; // Check function: returns true if ratio at K+x is >= ratio2 auto check_mid = [&](u128 current_k_plus_x) { // Ensure k value is valid if (current_k_plus_x < L) return false; u128 num = current_k_plus_x + 1; u128 den = current_k_plus_x + 1 - L; // Check denominator validity (should always be >= 1) if (den <= 0) return false; Ratio current_ratio = { num, den }; return current_ratio >= ratio2; }; // Check if K+0 is valid (it should be, as it's ratio1 >= ratio2) if (check_mid(current_K_128)) { max_x = 0; // Base case: 0 increments is valid low = 1; // Start searching for positive increments high = R_alpha[alpha_idx]; // Search up to the budget limit while(low <= high) { // Midpoint calculation avoiding overflow for large low/high u128 mid; // Check for potential overflow before addition if (high > low) { mid = low + (high - low) / 2; } else { mid = low; // or high, they are equal } if (mid == 0 && low > 0) break; // Prevent infinite loop if low becomes 1, mid=0. Should check mid range. // Use check_mid with K + mid if (check_mid(current_K_128 + mid)) { max_x = mid; // mid increments is possible, try larger low = mid + 1; // Need to check for overflow on mid+1 for u128? if (low == 0) break; // Overflow occurred if low wraps around } else { high = mid - 1; // mid increments is too much, try smaller } } } // else: initial check failed, indicates potential issue. max_x remains 0. // Number of increments to perform: max_x found + 1 (greedy step) u128 num_increments_128 = max_x + 1; // Clamp the number of increments by available budgets num_increments_128 = std::min(num_increments_128, (u128)R_alpha[alpha_idx]); num_increments_128 = std::min(num_increments_128, R_total_128); // If budget is positive but calculated increments is 0, force at least 1 increment if (num_increments_128 == 0 && R_alpha[alpha_idx] > 0 && R_total_128 > 0) { num_increments_128 = 1; } // If still 0 increments (e.g., budget exhausted), continue if (num_increments_128 == 0) continue; // Update k_value, budgets long long num_increments = (long long)num_increments_128; // Cast back to long long k_values[j1] += num_increments; R_alpha[alpha_idx] -= num_increments; R_total_128 -= num_increments_128; // Push the updated block back into the priority queue with its new ratio Ratio new_ratio = { (u128)k_values[j1] + 1, (u128)k_values[j1] + 1 - L }; pq.push({new_ratio, j1}); } // Calculate the final product of combinations modulo MOD Modint final_product = 1; for (int j = 0; j < p; ++j) { final_product *= nCr_mod(k_values[j], l_values[j]); } // Output the result std::cout << final_product.val << std::endl; return 0; }