Description

给定矩阵 \(A, B\) 和模数 \(p\), 求最小的 \(x\) 满足 \(A^x = B (\bmod p)\).

Input

第一行两个整数 \(n\)\(p\), 表示矩阵的阶和模数, 接下来一个 \(n \times n\) 的矩阵 \(A\). 接下来一个 \(n \times n\) 的矩阵 \(B\).

Output

输出一个正整数, 表示最小的可能的 \(x\), 数据保证在 \(p\) 内有解.

Sample Input

2 7
1 1
1 0
5 3
3 2

Sample Output

4

Data Range

对于 100% 的数据,\(n \leq 70, p \leq 19997, 0 \leq A_{i,j}, B_{i,j} \lt p\), 且满足 \(p\) 为质数.

同时保证 \(A\) 有逆矩阵.

Explanation

这道题用的大小步算法 (又称拔山盖世算法).

原文地址:http://blog.csdn.net/clover_hxy/article/details/50683832 (这一篇讲的是对于整数的大小步算法)

先把 x=i*m-j, 其中 m=ceil (sqrt (C)) , (ceil 是向上取整).

这样原式就变为 A^ (i*m-j)=B (mod C),

再变为 Aj×B=A (m*i) (mod C).

枚举 j (范围 0-m), 将 A^j×B 存入 hash 表

枚举 i (范围 1-m), 从 hash 表中寻找第一个满足 Aj×B=A (m*i) (mod C).

此时 x=i*m-j 即为所求.

在网上看到的其他题解大多用的是 x=im+j, 也可以做, 只是会牵扯的求逆元, 所以比较麻烦. 使 x=im-j 就可以轻松避免这个问题了.

那么肯定有人会有疑问为何只计算到 m=ceil (sqrt (C)) 就可以确定答案呢?

x=i*m-j 也就是 x 的最大值不会超过 p, 那超过 p 的怎么办?

有一个公式 a^ (k mod p)=a^k (mod p) 这个公式的推导需要用到费马小定理

k mod p 可以看做 k-mp, 原式可化成 ak/ (ap) m=ak (mod p)

根据费马小定理 a^p=1 (mod p) 其中 p 为质数, a, p 互质, 可得 ak/1m=a^k (mod p) ak=ak (mod p) 得证.

原文地址:http://blog.csdn.net/Lcomyn/article/details/46714569 (这篇内容和下一篇差不多, 而且没有那个详细)

原文地址:http://blog.csdn.net/xym_csdn/article/details/50675542

枚举 j, 接下来有两种方法, 一个是存哈希表, 但蒟蒻觉得太麻烦, 所以就学习了一下随机化, 随便搞一个 n×1 的矩阵 C, 在等式两边同乘, 变成 (Am) i×C=B×A^j×C (注意顺序! 不满足交换律!) , 然后枚举 i, 和得到的所有结果比较, 碰到就可以输出了

主过程时间复杂度: 存结果时 O (2n2√p) 判断时 O (√p (n2+√pn)) , 好像是这个吧

能让 n3 变成 n2, 原本跑 1. 5s 的程序就可以优化到 0. 6s 左右了==, 而且比 hash 快一些

Source Code


#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>

#define rep(_var,_begin,_end) for(int _var=_begin;_var<=_end;_var++)
using namespace std;
typedef long long lli;
const int maxn = 80, maxm = 150; // maxm^2 > p

int n, m, modr;

struct matrix {
    lli val[maxn][maxn];
    matrix& operator *= (matrix b) {
        static lli c[maxn][maxn];
        rep(i, 1, n) rep(j, 1, n) {
            c[i][j] = 0;
            rep(k, 1, n) c[i][j] += val[i][k] * b.val[k][j];
            c[i][j] %= modr;
        }
        rep(i, 1, n) rep(j, 1, n)
            val[i][j] = c[i][j];
        return *this;
    }
    matrix(int v) {
        rep(i, 1, n) rep(j, 1, n)
            val[i][j] = i == j ? v : 0;
        return ;
    }
    matrix(void) {
        rep(i, 1, n) rep(j, 1, n)
            val[i][j] = 0;
        return ;
    }
    void print(void) {
        rep(i, 1, n) rep(j, 1, n)
            printf("%d ", val[i][j]);
        printf("\n");
        return ;
    }
};

matrix operator ^ (matrix a, lli powr)
{
    matrix res(1);
    for (powr; powr > 0; powr >>= 1, a *= a)
        if (powr & 1)
            res *= a;
    return res;
}

// A magic comparator that compares the hashes between hashed multiplication
// results
bool operator == (matrix a, matrix b)
{
    rep(i, 1, n) if (a.val[i][1] != b.val[i][1])
        return false;
    return true;
}

// A magic multiplication that hashes the matrix multiplication, avoiding
// excessive computation as we don't require the exact results
matrix operator * (matrix a, matrix b)
{
    matrix c;
    rep(i, 1, n) {
        rep(k, 1, n) c.val[i][1] += a.val[i][k] * b.val[k][1];
        c.val[i][1] %= modr;
    }
    return c;
}

matrix hash[maxm];

int main(int argc, char** argv)
{
    scanf("%d%d", &n, &modr);
    matrix a, b, c__, c;
    rep(i, 1, n) rep(j, 1, n)
        scanf("%lld", &a.val[i][j]);
    rep(i, 1, n) rep(j, 1, n)
        scanf("%lld", &b.val[i][j]);
    // Baby step giant step
    m = sqrt(modr);
    rep(i, 1, n)
        c__.val[i][1] = i % modr;
    c = c__;
    for (int i = 0; i <= m; i++) {
        hash[i] = b * c;
        c = a * c;
    }
    a = a ^ m;
    c = c__;
    int res = 0;
    for (int i = 1; i <= m; i++) {
        c = a * c;
        for (int j = 0; j <= m; j++)
            if (hash[j] == c) {
                res = i * m - j;
                i = m + 1;
                break;
            }
    }
    printf("%d\n", res);
    return 0;
}