归档:220813 | 社恐铁锹的第一次新知讲授:矩阵快速幂

铁锹:呃,其实我的名字是英文缩写,不是铁锹,你们不要再给我乱起外号了


因为之前已经在洛谷上写过一篇关于矩乘的总结(不过好像丢了),所以这次就接着上次写吧。


什么是矩阵?

一个 \(n\times m\) 的矩阵就是一个由数组成的 \(n\)\(m\) 列的方阵。

什么是矩阵乘法?

假设有三个矩阵 \(A, B, C\),其中 \(A\) 的列数与 \(B\) 的行数相等(假设为 \(n\))。若 \(C=A\times B\),那么 \(C_{i,j}=\sum\limits_{k=1}^n A_{i,k}\times B_{k,j}\),其中 \(C\) 的行数与 \(A\) 的行数相等,\(C\) 的列数与 \(B\) 的列数相等。

什么是矩阵快速幂?

把普通快速幂的底数换成矩阵。

铁锹:呃我要讲的就是这些,同学们可以开始做题了。


呃呃,所以模拟一下这个过程就好了?

namespace Matrix {
const int maxn = 25;
template<const int mod>
class Mat {
private:
    int n, m;
public:
    int a[maxn][maxn];
    Mat () {}
    Mat (int N, int M, bool type = 0) {
        n = N, m = M;
        memset(a, 0, sizeof (a));
        if (type) {
            for (int i = 1; i <= n; ++i)
                a[i][i] = 1;
        }
    }
    int* operator[] (int q) {
        return a[q];
    }
    const int* operator[] (int q) const {
        return a[q];
    }
    Mat operator* (const Mat &q) const {
        Mat res(n, q.m);
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= q.m; ++j) {
                for (int k = 1; k <= m; ++k) {
                    (res[i][j] += (a[i][k] 
                        * q[k][j] % mod + mod)
                        % mod) %= mod;
                }
            }
        }
        return res;
    }
    Mat& operator*= (const Mat &q) {
        return (*this) = (*this) * q;
    }
    Mat operator^ (const int q) const {
        int y = q;
        Mat res(n, n, 1), x(*this);
        while (y) {
            if (y & 1)
                res *= x;
            x *= x;
            y >>= 1;
        }
        return res;
    }
    Mat& operator^= (const int &q) {
        return (*this) = (*this) ^ q;
    }
};
} // namespace Matrix

A. CF450B - Jzzhu and Sequences

游戏的第一关都会放一个稀奇古怪的东西让新手入门。

——Some TFers

我看了大半天没看懂样例最后发现不是斐波那契???

那也和斐波那契一样啊。

我们都知道,斐波那契的加速矩阵是这样的:

\[[f_i,f_{i + 1}]=[f_{i - 1},f_i]\times\begin{bmatrix}0&1\\1&1 \end{bmatrix} \]

那我们把 \(f_{i-1}\to f_{i+1}\) 的系数改成减就好了。

namespace XSC062 {
using namespace fastIO;
using namespace Matrix;
const int mod = 1e9 + 7;
int x, y, m;
Mat<mod> A, B;
int main() {
    read(x);
    read(y);
    read(m);
    if (m == 1) {
        printf("%lld", (x % mod + mod) % mod);
        return 0;
    }
    if (m == 2) {
        printf("%lld", (y % mod + mod) % mod);
        return 0;
    }
    A = Mat<mod>(1, 2);
    B = Mat<mod>(2, 2);
    A[1][1] = x;
    A[1][2] = y;
    B[1][2] = -1;
    B[2][1] = B[2][2] = 1;
    A *= (B ^= (m - 2));
    printf("%lld", A[1][2]);
    return 0;
}
} // namespace XSC062

B. CF621E - Wet Shark and Blocks

它是一道非常奇怪的题啊,为什么我倒推过不了,顺推能过啊 🤔

首先统计所有模 \(x\) 的值为 \(i\) 的个数,记为 \(cnt_i\)

\(f_{i,j}\) 表示选到了第 \(i\) 组数后选的数模 \(x\)\(j\) 的方案,然后滚一下,\(f_i\) 就直接表示选到某一组模 \(x\)\(j\) 的方案。

那么容易得出:

\[f_{(i\times10 + j)\bmod x} = f_i \times cnt_j \]

然后 \(f\) 的初始值就是这个 \(cnt\) 序列啦。

但是 \(b\) 这么大,明显不可能一组一组地去推,我们就把这个式子搬到加速矩阵上面,从 \(i\) 转移到 \((i\times10+j)\bmod x\) 的位置对应的值就是 \(cnt_j\)

然后令初始矩阵为 \(cnt\),就完了。

namespace XSC062 {
using namespace fastIO;
using namespace Matrix;
const int mod = 1e9 + 7;
const int maxn = 5e5 + 5;
Mat<mod> A, B;
int n, b, k, x, t;
int a[maxn], cnt[maxn];
inline int qkp(int x, int y, int mod) {
    int res = 1;
    while (y) {
        if (y & 1)
            (res *= x) %= mod;
        (x *= x) %= mod;
        y >>= 1;
    }
    return res;
}
int main() {
    read(n);
    read(b);
    read(k);
    read(x);
    for (int i = 1; i <= n; ++i) {
        read(a[i]);
        ++cnt[a[i] % x];
    }
    A = Mat<mod>(1, x, 0);
    for (int i = 1; i <= x; ++i)
        A[1][i] = cnt[i - 1];
    B = Mat<mod>(x, x, 0);
    for (int i = 0; i < x; ++i) {
        for (int j = 0; j < x; ++j) {
            t = (i * 10 + j) % x;
            B[i + 1][t + 1] += cnt[j];
        }
    }
    B.print();
    A *= (B ^ (b - 1));
    printf("%lld", A[1][k + 1]);
    return 0;
}
} // namespace XSC062
posted @ 2022-08-16 00:02  XSC062  阅读(35)  评论(0编辑  收藏  举报