結果
| 問題 |
No.3038 シャッフルの再現
|
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:13:31 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
TLE
|
| 実行時間 | - |
| コード長 | 10,915 bytes |
| コンパイル時間 | 1,365 ms |
| コンパイル使用メモリ | 106,704 KB |
| 実行使用メモリ | 9,344 KB |
| 最終ジャッジ日時 | 2025-05-14 13:14:33 |
| 合計ジャッジ時間 | 7,588 ms |
|
ジャッジサーバーID (参考情報) |
judge1 / judge4 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | -- * 1 |
| other | TLE * 1 -- * 20 |
ソースコード
#include <iostream>
#include <vector>
#include <numeric>
#include <map>
#include <cmath> // For std::abs, std::sqrt
#include <cstdlib> // For std::rand, std::srand
#include <ctime> // For std::time
#include <numeric> // For std::gcd
#include <algorithm> // For std::max
// Use __int128 for intermediate calculations where standard long long might overflow
using ll = long long;
// The modulus for the final answer
ll P_MOD = 1e9 + 7;
// Modular exponentiation: computes (base^exp) % P_MOD
ll power(ll base, ll exp) {
ll res = 1;
base %= P_MOD;
if (base < 0) base += P_MOD; // Ensure base is non-negative
while (exp > 0) {
if (exp % 2 == 1) res = (__int128)res * base % P_MOD;
// Avoid unnecessary squaring at the end when exp becomes 1
if (exp > 1) {
base = (__int128)base * base % P_MOD;
}
exp /= 2;
}
return res;
}
// Modular exponentiation: computes (base^exp) % m for a specific modulus m
ll power_mod(ll base, ll exp, ll m) {
if (m == 1) return 0; // Modulo 1 is always 0
__int128 res = 1;
base %= m;
if (base < 0) base += m; // Ensure base is non-negative
while (exp > 0) {
if (exp % 2 == 1) res = res * base % m;
if (exp > 1) {
base = (__int128)base * base % m;
}
exp /= 2;
}
return (ll)res;
}
// Matrix struct for 2x2 matrices used in Fibonacci calculations
struct Matrix {
ll mat[2][2];
Matrix() {
mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = 0;
}
// Returns the identity matrix
static Matrix identity() {
Matrix I;
I.mat[0][0] = I.mat[1][1] = 1;
return I;
}
// Matrix multiplication with modulo m
Matrix multiply(const Matrix& other, ll m) const {
Matrix result;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < 2; ++k) {
// Use __int128 to prevent overflow during multiplication
result.mat[i][j] = (result.mat[i][j] + (__int128)mat[i][k] * other.mat[k][j]);
// Keep intermediate values within check to avoid large numbers before final modulo
if (result.mat[i][j] >= 2*m || result.mat[i][j] <= -2*m)
result.mat[i][j] %= m;
}
// Apply final modulo and ensure non-negativity
result.mat[i][j] %= m;
if (result.mat[i][j] < 0) result.mat[i][j] += m;
}
}
return result;
}
// Equality comparison for matrices
bool operator==(const Matrix& other) const {
return mat[0][0] == other.mat[0][0] && mat[0][1] == other.mat[0][1] &&
mat[1][0] == other.mat[1][0] && mat[1][1] == other.mat[1][1];
}
};
// Matrix exponentiation: computes (base^exp) % m
Matrix matrix_pow(Matrix base, ll exp, ll m) {
if (m == 1) return Matrix(); // Return zero matrix for mod 1
Matrix res = Matrix::identity();
while (exp > 0) {
if (exp % 2 == 1) res = res.multiply(base, m);
base = base.multiply(base, m);
exp /= 2;
}
return res;
}
// Miller-Rabin primality test
// Bases {2, 3, 5, 7, 11} are sufficient for N < 25,326,001
// which covers the max required bound of 2 * (10^7 + 1)
bool is_prime(ll n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
ll d = n - 1;
while (d % 2 == 0) d /= 2;
auto check = [&](ll a) {
ll x = power_mod(a, d, n);
if (x == 1 || x == n - 1) return true;
ll current_d = d;
while (current_d != n - 1) {
x = (__int128)x * x % n;
current_d *= 2;
if (x == 1) return false; // Found non-trivial sqrt of 1 mod n
if (x == n - 1) return true;
}
return false; // Failed witness test
};
for (ll a : {2, 3, 5, 7, 11}) {
if (n == a) return true; // n is one of the bases
if (a >= n) break; // Don't test bases larger than n
if (!check(a)) return false; // Found a witness, n is composite
}
return true; // Likely prime
}
// Pollard's Rho algorithm for integer factorization
// Returns a non-trivial factor of n, or -1 on failure (e.g., finding n itself)
// Takes starting c value to allow retries with different parameters
ll pollard_rho(ll n, ll c_start) {
ll x = 2, y = 2, d = 1;
ll c = c_start; // Parameter for the pseudo-random function
// The function g(x) = (x^2 + c) mod n
auto f = [&](ll val) {
__int128 temp = (__int128)val * val;
temp = (temp + c) % n;
return (ll)temp;
};
// Floyd's cycle-finding algorithm
while (d == 1) {
x = f(x);
y = f(f(y)); // y moves twice as fast as x
d = std::gcd(std::abs(x - y), n); // Check GCD periodically
}
// If d == n, Pollard's Rho failed with this c. Caller should retry.
if (d == n) return -1;
return d; // Found a non-trivial factor d
}
// Optimized factorization using trial division + Pollard Rho
std::map<ll, int> factorize_optimized(ll n) {
std::map<ll, int> factors;
if (n <= 1) return factors;
ll current_n = n;
// Step 1: Trial division for small primes up to a limit (e.g., 1000)
ll limit = 1000;
for (ll i = 2; i <= limit && i * i <= current_n; ++i) {
if (current_n % i == 0) {
while (current_n % i == 0) {
factors[i]++;
current_n /= i;
}
}
}
// If fully factored, return
if (current_n == 1) return factors;
// Step 2: If n is still large, use Miller-Rabin + Pollard's Rho
std::vector<ll> q; // Stack for factors to process
q.push_back(current_n);
ll c_pollard = 1; // Initial c value for Pollard's Rho
while(!q.empty()){
ll curr = q.back();
q.pop_back();
if (curr == 1) continue;
// Check if the current number is prime
if(is_prime(curr)){
factors[curr]++;
continue;
}
// If composite, find a factor using Pollard's Rho
ll factor = pollard_rho(curr, c_pollard);
// Retry mechanism if Pollard's Rho fails (returns -1)
while(factor == -1) {
c_pollard++; // Try next c value
factor = pollard_rho(curr, c_pollard);
}
// Push the found factor and the remaining part onto the stack
if (factor > 1) { // Ensure factor is valid
q.push_back(factor);
q.push_back(curr / factor);
} else {
// This indicates a critical failure, potentially a bug
std::cerr << "Factorization critical failure for: " << curr << std::endl;
// As a dangerous fallback, consider curr might be a prime power or issue with implementation
factors[curr]++; // Add curr itself, likely incorrect but avoids crash
}
}
return factors;
}
// Helper function to combine factor maps for LCM calculation
// Updates total_factors with maximum exponents from new_factors
void combine_factors(std::map<ll, int>& total_factors, const std::map<ll, int>& new_factors) {
for (const auto& pair : new_factors) {
total_factors[pair.first] = std::max(total_factors.count(pair.first) ? total_factors[pair.first] : 0, pair.second);
}
}
// Find Pisano period pi(p) for prime p
ll find_pisano_period(ll p) {
// Handle base cases
if (p == 2) return 3;
if (p == 5) return 20;
// Determine the upper bound for the period based on p mod 5
ll bound;
ll p_mod_5 = p % 5;
if (p_mod_5 == 1 || p_mod_5 == 4) { // Case Legendre symbol (5/p) = 1
bound = p - 1;
} else { // Case Legendre symbol (5/p) = -1
bound = 2 * (p + 1);
}
// Factorize the bound
std::map<ll, int> bound_factors = factorize_optimized(bound);
ll period = bound; // Start with the maximal possible period
Matrix T; // Fibonacci matrix T = {{1, 1}, {1, 0}}
T.mat[0][0] = 1; T.mat[0][1] = 1;
T.mat[1][0] = 1; T.mat[1][1] = 0;
Matrix I = Matrix::identity(); // Identity matrix
// Iterate through prime factors of the bound to find the minimal period
for (auto const& [q, e] : bound_factors) {
// Check if period is divisible by q before trying to reduce it
if (period % q != 0) continue;
ll test_period = period / q;
Matrix T_pow = matrix_pow(T, test_period, p); // Compute T^(period/q) mod p
// While T^(period/q) is Identity, it means the period is smaller. Reduce period.
while (T_pow == I) {
period = test_period;
// Check if further division by q is possible
if (period % q != 0) break;
test_period /= q;
if (period == 0) { // Safety check, should not happen
std::cerr << "Error: Period became zero." << std::endl;
return 1; // Return 1 as a fallback period?
}
T_pow = matrix_pow(T, test_period, p);
}
}
return period; // The final minimal period
}
int main() {
// Faster I/O
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
// Seed random number generator for Pollard's Rho (optional if using deterministic c start)
// std::srand(std::time(0));
int N; // Number of prime factors of M
std::cin >> N;
// Map to store the prime factorization of the final LCM
std::map<ll, int> total_factors;
for (int i = 0; i < N; ++i) {
ll p; // Prime factor of M
int k; // Exponent of p in M's factorization
std::cin >> p >> k;
// Find the Pisano period for the prime p
ll pi_p = find_pisano_period(p);
// Factorize pi(p)
std::map<ll, int> current_pi_factors = factorize_optimized(pi_p);
// Apply the formula pi(p^k) = p^(k-1) * pi(p)
// Update the factors map by adding p with exponent k-1
if (k > 1) {
current_pi_factors[p] += (k - 1);
}
// Combine the factors into the total factors map for LCM calculation
combine_factors(total_factors, current_pi_factors);
}
// Compute the final LCM modulo P_MOD from its prime factorization
ll final_lcm = 1;
for (auto const& [prime_factor, exponent] : total_factors) {
if (exponent == 0) continue; // Skip factors with exponent 0
// Compute prime_factor^exponent mod P_MOD
ll term = power(prime_factor, exponent);
// Multiply into the final LCM result
final_lcm = (__int128)final_lcm * term % P_MOD;
}
// Output the final result
std::cout << final_lcm << std::endl;
return 0;
}
qwewe