結果
| 問題 |
No.603 hel__world (2)
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 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;
}
qwewe