結果

問題 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
      |                      ~~~~~^~~~~~

ソースコード

diff #

#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;
}
0