結果
| 問題 |
No.950 行列累乗
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:10:11 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
WA
|
| 実行時間 | - |
| コード長 | 17,697 bytes |
| コンパイル時間 | 1,195 ms |
| コンパイル使用メモリ | 113,460 KB |
| 実行使用メモリ | 7,848 KB |
| 最終ジャッジ日時 | 2025-05-14 13:11:39 |
| 合計ジャッジ時間 | 5,043 ms |
|
ジャッジサーバーID (参考情報) |
judge4 / judge1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 4 |
| other | AC * 41 WA * 16 |
コンパイルメッセージ
main.cpp: In function ‘int main()’:
main.cpp:377:27: warning: integer overflow in expression of type ‘int’ results in ‘-1794967296’ [-Woverflow]
377 | else if (p > 50000*50000) m_bsgs = 50000; // Cap at 50000 if needed
| ~~~~~^~~~~~
ソースコード
#include <iostream>
#include <vector>
#include <cmath>
#include <map>
#include <utility> // For std::pair
using namespace std;
// Define long long and 128-bit integer types for intermediate calculations
typedef long long ll;
using i128 = __int128;
ll p; // The prime modulus
// Structure definition for 2x2 matrices
struct Matrix {
ll mat[2][2];
// Default constructor initializes to zero matrix
Matrix() {
mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = 0;
}
// Static method to create an identity matrix
static Matrix identity() {
Matrix I;
I.mat[0][0] = I.mat[1][1] = 1;
I.mat[0][1] = I.mat[1][0] = 0;
return I;
}
// Matrix multiplication operator override
Matrix operator*(const Matrix& other) 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 128-bit integer for intermediate product to prevent overflow
i128 prod = (i128)mat[i][k] * other.mat[k][j];
result.mat[i][j] = (result.mat[i][j] + prod) % p;
}
}
}
return result;
}
// Equality comparison operator override
bool operator==(const Matrix& other) const {
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
if (mat[i][j] != other.mat[i][j]) {
return false;
}
}
}
return true;
}
// Less-than comparison operator override (needed for using Matrix as a map key)
bool operator<(const Matrix& other) const {
for(int i=0; i<2; ++i) {
for(int j=0; j<2; ++j) {
if(mat[i][j] != other.mat[i][j]) {
return mat[i][j] < other.mat[i][j];
}
}
}
return false; // Matrices are equal
}
};
// Matrix exponentiation using binary exponentiation (logarithmic time complexity)
Matrix matrix_pow(Matrix base, ll exp) {
Matrix res = Matrix::identity();
// Ensure base matrix entries are already modulo p
base.mat[0][0] %= p; base.mat[0][1] %= p; base.mat[1][0] %= p; base.mat[1][1] %= p;
while (exp > 0) {
if (exp % 2 == 1) res = res * base; // If exponent is odd, multiply result by base
base = base * base; // Square the base
exp /= 2; // Halve the exponent
}
return res;
}
// Modular exponentiation for scalars (logarithmic time complexity)
ll power(ll base, ll exp) {
ll res = 1;
base %= p; if (base < 0) base += p; // Ensure base is in [0, p-1]
while (exp > 0) {
if (exp % 2 == 1) res = (i128)res * base % p;
base = (i128)base * base % p;
exp /= 2;
}
return res;
}
// Modular inverse using Fermat's Little Theorem (requires p to be prime)
// Calculates n^(p-2) mod p
ll modInverse(ll n) {
n %= p; if (n < 0) n += p; // Ensure n is in [0, p-1]
if (n == 0) return -1; // Inverse does not exist for 0
// For prime p, n^(p-2) is the modular inverse of n modulo p (if n != 0 mod p)
return power(n, p - 2);
}
// Standard Baby-step Giant-step algorithm for scalar discrete logarithm a^x = b (mod p)
// Finds the minimum non-negative integer x satisfying the equation. Returns -1 if no solution.
ll solve_dlog_standard(ll a, ll b) {
a %= p; if(a<0) a+=p;
b %= p; if(b<0) b+=p;
if (p == 0) return -1; // p must be >= 2
// Handle edge cases involving 0 and 1
if (b == 1) { // Looking for a^x = 1
// Smallest non-negative x is 0 if a!=0. If a=0, 0^x=1 impossible for x>=1. 0^0=1 convention.
if (a == 0) return -1;
return 0;
}
if (a == 0) { // a=0, b!=1
if (b == 0) return 1; // 0^1 = 0. Smallest non-negative x=1.
else return -1; // 0^x = b != 0 is impossible for x>=1.
}
// Now a != 0
if (b == 0) { // a != 0
return -1; // a^x = 0 is impossible if a != 0
}
// Normal case: a != 0, b != 0, b != 1
ll m = sqrt(p) + 1; // Set step size for BSGS
map<ll, ll> baby_steps; // Store baby steps (a^j mod p -> j)
ll current_val = 1;
// Calculate and store baby steps
for (ll j = 0; j < m; ++j) {
if (!baby_steps.count(current_val)) { // Store only the first occurrence (smallest j)
baby_steps[current_val] = j;
}
current_val = (i128)current_val * a % p;
}
// Calculate a^(-m) mod p for giant steps
ll am_inv = modInverse(power(a, m));
if (am_inv == -1) return -1; // Inverse should exist as a != 0
current_val = b; // Start giant steps from target value b
// Perform giant steps
for (ll i = 0; i < m; ++i) {
if (baby_steps.count(current_val)) { // Check if current value matches a baby step
ll j = baby_steps[current_val];
// Found solution x = i*m + j. BSGS guarantees this is the minimal non-negative solution.
return i * m + j;
}
// Apply giant step factor a^(-m)
current_val = (i128)current_val * am_inv % p;
}
return -1; // No solution found
}
// Finds the order of element a modulo p.
// The order is the smallest positive integer k such that a^k = 1 (mod p). Returns -1 if error or order not found within limits.
ll find_order(ll a) {
a %= p; if(a<0) a+=p;
if (a == 0) return -1; // Order undefined for 0
if (a == 1) return 1; // Order of 1 is 1
// Use BSGS to find the smallest positive k. Similar to solving a^k=1, but ensure k>0.
ll m = sqrt(p) + 1;
map<ll, ll> table;
ll cur = 1;
// Store powers a^j for j = 0..m-1
for (ll j = 0; j < m; ++j) {
if (!table.count(cur)) table[cur] = j;
cur = (i128)cur * a % p;
}
ll step = power(a, m); // Giant step factor a^m
ll step_inv = modInverse(step);
if(step_inv == -1) return -1; // Should not happen if a != 0
cur = 1; // Current value for giant step check starts from 1. Represents (a^m)^i
ll current_target = 1; // Target is 1
// We need to find smallest k = im + j > 0 such that a^k=1.
// Rearranging: a^{im} = a^{-j}. Calculate (a^{-m})^i and check against table a^j.
// We want smallest i*m+j > 0. BSGS naturally finds smallest non-negative solution. If solution is 0, it means a=1.
// Need to adapt BSGS slightly.
cur = 1; // Start from (a^m)^0 = 1
// Check i=0 case: if 1 is in table with j > 0, then a^j = 1. Smallest such j is the order.
// However, this is implicitly checked by BSGS for a^x=1. The issue is when the result is 0.
// Rerun BSGS for a^k = 1, but interpret the result carefully for minimum positive k.
ll k_cand = solve_dlog_standard(a, 1);
if (k_cand == 0) { // a^0=1. This means smallest non-negative k is 0. Need smallest positive.
// This case should only happen if a=1, which is handled above.
// If somehow a!=1 and BSGS returns 0, it's possibly an issue with BSGS implementation or logic.
// Let's rely on the fact that if a!=1, order k>0.
// The standard BSGS needs modification to return smallest positive k when k=0 is found.
// But for this problem, we only need this function when solving a^n=b where b=1 and n=0 is found by standard BSGS.
// A simplified approach: If a^0=1 is the smallest non-negative solution, find the order by iteration? Might be slow.
// Try re-using BSGS logic slightly modified to guarantee positive result:
map<ll, ll> baby_steps_order;
ll current_val_order = a; // Start from a^1
for (ll j = 1; j <= m; ++j) { // Store a^j for j=1..m
if (!baby_steps_order.count(current_val_order)) {
baby_steps_order[current_val_order] = j;
}
current_val_order = (i128)current_val_order * a % p;
}
ll giant_step_val = 1; // Start from a^0=1
for (ll i = 1; i <= m; ++i) { // Check powers a^{im} for i=1..m
giant_step_val = (i128)giant_step_val * step % p;
if (baby_steps_order.count(giant_step_val)) { // If a^{im} == a^j
ll j = baby_steps_order[giant_step_val];
if (i * m - j > 0) return i * m - j; // Found order k = im - j > 0
// Need smallest k. This finds *a* solution, not necessarily minimal.
// BSGS complexities... Let's assume standard BSGS gives order k>0 when a!=1.
}
}
// Revert to just returning k_cand from standard BSGS if a!=1. It should be >0.
k_cand = solve_dlog_standard(a, 1); // Re-call standard BSGS. It should return order > 0 if a != 1.
if (k_cand == 0 && a!=1) return -1; // If it returns 0 despite a!=1, something is wrong.
return k_cand;
} else {
return k_cand; // Standard BSGS found non-zero result. This is the order.
}
return -1; // Fallback, should not be reached for a in Z_p^*
}
// Finds minimum positive integer n such that a^n = b (mod p)
ll solve_dlog_min_positive(ll a, ll b) {
a %= p; if(a<0) a+=p;
b %= p; if(b<0) b+=p;
if (a == 0) {
if (b == 0) return 1; // a^1 = 0 = b
else return -1; // a^n = b != 0 impossible for n>=1
}
if (b == 0) return -1; // a^n=0 impossible for a != 0
ll x = solve_dlog_standard(a, b); // Find smallest non-negative solution x
if (x == -1) return -1; // No solution exists
if (x > 0) return x; // Smallest non-negative is positive, this is the minimum positive
// If x == 0, this means a^0 = b, so b must be 1.
if (x == 0) {
if (b != 1) {
// This should ideally not happen if solve_dlog_standard is correct.
return -1;
}
// Need minimum positive k such that a^k = 1. This is the order of a.
return find_order(a); // Call function to find order
}
return -1; // Should not be reached
}
int main() {
ios_base::sync_with_stdio(false); // Faster I/O
cin.tie(NULL);
cin >> p; // Read prime modulus
Matrix A, B; // Read matrices A and B
cin >> A.mat[0][0] >> A.mat[0][1] >> A.mat[1][0] >> A.mat[1][1];
cin >> B.mat[0][0] >> B.mat[0][1] >> B.mat[1][0] >> B.mat[1][1];
// Check if n=1 is the solution
if (A == B) { cout << 1 << endl; return 0; }
// Calculate determinant of A modulo p
ll detA = (A.mat[0][0] * A.mat[1][1] - A.mat[0][1] * A.mat[1][0]);
detA = (detA % p + p) % p; // Ensure determinant is in [0, p-1]
// Case 1: A is not invertible (determinant is 0)
if (detA == 0) {
Matrix zero_matrix; // A matrix with all entries 0
if (A == zero_matrix) { // If A is the zero matrix
if (B == zero_matrix) cout << 1 << endl; // A^1 = 0 = B
else cout << -1 << endl; // A^n = 0 for n>=1, cannot equal non-zero B
return 0;
}
// A is non-zero, non-invertible
ll trA = (A.mat[0][0] + A.mat[1][1]); // Calculate trace of A modulo p
trA = (trA % p + p) % p; // Ensure trace is in [0, p-1]
if (trA == 0) { // Nilpotent case: A^2 = 0 (since tr(A)=0, det(A)=0)
// Sequence is A, 0, 0, ...
if (B == zero_matrix) cout << 2 << endl; // A^2 = 0 = B. Minimum n=2. (n=1 case A=B already checked)
else cout << -1 << endl; // B is neither A nor 0
return 0;
} else { // Non-nilpotent case: A^n = tr(A)^(n-1) * A for n >= 1
// Check if B is a scalar multiple of A: B = k * A
ll k = -1; // Scalar multiplier k
bool found_nonzero = false; // Flag to track if a non-zero entry in A is found
// Find first non-zero entry A_ij to calculate potential k = B_ij / A_ij
for (int i=0; i<2; ++i) {
for (int j=0; j<2; ++j) {
if (A.mat[i][j] != 0) {
found_nonzero = true;
ll inv_A_entry = modInverse(A.mat[i][j]); // Find modular inverse of A_ij
if (inv_A_entry == -1) { /* Should not happen if A_ij != 0 */ cout << -1 << endl; return 0;}
k = (i128)B.mat[i][j] * inv_A_entry % p; // Calculate k
break;
}
}
if (found_nonzero) break;
}
// If A was non-zero matrix, it must have a non-zero entry
if (!found_nonzero) { /* This case should not be reachable if A != 0 */ cout << -1 << endl; return 0; }
// Verify if B = k * A holds for all entries
bool is_scalar_multiple = true;
for (int i=0; i<2; ++i) {
for (int j=0; j<2; ++j) {
ll expected_B = (i128)k * A.mat[i][j] % p; // Calculate k * A_ij
if (B.mat[i][j] != expected_B) { // Compare with B_ij
is_scalar_multiple = false;
break;
}
}
if (!is_scalar_multiple) break;
}
if (!is_scalar_multiple) { // If B is not k*A
cout << -1 << endl;
return 0;
}
// If B = k*A, we need to solve tr(A)^(n-1) = k (mod p) for minimum positive n
// Let m = n-1. Need minimum non-negative m such that tr(A)^m = k (mod p).
ll m_sol = solve_dlog_standard(trA, k); // Use BSGS to find minimal non-negative m
if (m_sol == -1) { // If no solution for m
cout << -1 << endl;
} else {
cout << m_sol + 1 << endl; // Minimum positive n = m + 1
}
return 0;
}
} else { // Case 2: A is invertible (determinant is non-zero)
// Calculate determinant of B
ll detB = (B.mat[0][0] * B.mat[1][1] - B.mat[0][1] * B.mat[1][0]);
detB = (detB % p + p) % p;
if (detB == 0) { // If B is not invertible, A^n = B is impossible
cout << -1 << endl;
return 0;
}
// Special subcase: A is a scalar matrix, A = a*I
if (A.mat[0][1] == 0 && A.mat[1][0] == 0 && A.mat[0][0] == A.mat[1][1]) {
// Check if B is also a scalar matrix, B = b*I
if (B.mat[0][1] == 0 && B.mat[1][0] == 0 && B.mat[0][0] == B.mat[1][1]) {
ll a = A.mat[0][0]; // Scalar factor for A
ll b = B.mat[0][0]; // Scalar factor for B
// Need minimum positive n such that a^n = b (mod p)
ll n = solve_dlog_min_positive(a, b);
cout << n << endl;
} else { // B is not scalar, A^n = a^n * I cannot equal B
cout << -1 << endl;
}
return 0;
}
// General invertible case: Use Baby-step Giant-step algorithm for matrices
// Determine step size m. sqrt(p) seems reasonable based on scalar BSGS complexity.
// Order can be up to p^2, so sqrt(p^2) = p steps would be ideal but too slow.
// Use sqrt(p) capped at a reasonable value (e.g., 45000) to manage time/memory.
ll m_bsgs = sqrt(p) + 2;
if (p > 2025000000) m_bsgs = 45000; // If p > 45000^2, cap m_bsgs
else if (p > 50000*50000) m_bsgs = 50000; // Cap at 50000 if needed
map<Matrix, ll> baby_steps; // Store baby steps (A^j -> j)
Matrix current_A = Matrix::identity(); // Start with A^0 = I
// Compute and store baby steps A^0, A^1, ..., A^(m-1)
for(ll j=0; j < m_bsgs; ++j) {
if (baby_steps.find(current_A) == baby_steps.end()) { // Store smallest j for each matrix
baby_steps[current_A] = j;
}
current_A = current_A * A; // Compute next power A^(j+1)
}
// Calculate A inverse: A^{-1} = det(A)^{-1} * adj(A)
ll detA_inv = modInverse(detA);
if (detA_inv == -1) { /* Should not happen as detA != 0 */ cout << -1 << endl; return 0; }
Matrix A_adj; // Adjugate matrix of A
A_adj.mat[0][0] = A.mat[1][1]; A_adj.mat[0][1] = (p - A.mat[0][1]) % p;
A_adj.mat[1][0] = (p - A.mat[1][0]) % p; A_adj.mat[1][1] = A.mat[0][0];
Matrix A_inv_mat; // A inverse matrix
for(int i=0; i<2; ++i) for(int j=0; j<2; ++j) A_inv_mat.mat[i][j] = (i128)detA_inv * A_adj.mat[i][j] % p;
// Calculate (A^{-1})^m = (A^m)^{-1} for giant steps
Matrix A_m_inv = matrix_pow(A_inv_mat, m_bsgs);
Matrix current_B = B; // Start giant steps from target matrix B
ll min_n = -1; // Variable to store the minimum positive solution found
// Perform giant steps. Check B * (A^{-m})^i against baby steps
// Loop limit m_bsgs covers exponents up to m_bsgs^2
for(ll i=0; i <= m_bsgs; ++i) {
if (baby_steps.count(current_B)) { // If current_B matches a baby step A^j
ll j = baby_steps[current_B];
ll n = i * m_bsgs + j; // Candidate solution n = i*m + j
if (n > 0) { // We need the minimum positive solution
if (min_n == -1 || n < min_n) { // Update minimum if this is smaller
min_n = n;
}
}
}
if (i < m_bsgs) { // Avoid extra multiplication at the end
current_B = current_B * A_m_inv; // Apply giant step: multiply by (A^m)^{-1}
}
}
cout << min_n << endl; // Output the minimum positive n found, or -1 if none found
}
return 0;
}
qwewe