矩阵

定义

只有\(A的行数\)\(B的列数\)相等,这两个矩阵才可以相乘

\(A\)是一个\(n\times p\)的矩阵,\(B\)是一个\(p\times m\)的矩阵。

\(A\times B=C\),那么\(C\)肯定是一个\(n\times m\)的矩阵。

矩阵\(C\)中的​元素可以表示为

\[C_{i,j}=\sum_{k=1}^PA_{i,k}B_{k,j} \]

举个栗子:

\[\left[\begin{matrix}3&3&7&8 \\\end{matrix}\right]\times\left[\begin{matrix}4\\5\\8\\1\\8\end{matrix}\right]=\left[\begin{matrix}12&15&24&3&24\\12&15&24&3&24\\28&35&56&7&56\\32&40&64&8&64\\\end{matrix}\right] \]

国家级冠军的必备千位数加减!

矩阵乘法不满足交换律(不用说都知道),但是满足结合律。

也很好证明。

\(A\times B\times C=D\)

\(A\times B=T\)

\[D_{i,j}=\sum_{x=1}^qT_{i,x}\times C_{x,j}\\D_{i,j}=\sum_{x=1}^q(\sum_{y=1}^pA_{i,y}\times B_{y,x})\times C_{x,j}\\D_{i,j}=\sum_{x=1}^q\sum_{y=1}^pA_{i,y}\times B_{y,x}\times C_{x,j} \]

\(A\times (B\times C)=E\)

同理得

\[E_{i,j}=\sum_{x=1}^p\sum_{y=1}^qA_{i,x}\times B_{x,y}\times C_{y,j} \]

可得\(D=E\)

方阵

一个\(n\times n\)的矩阵

因为行数和列数相等,所以方阵的幂有意义。

我们就可以利用快速幂优化方阵。

。。。

优化线性递推

求斐波那契数列的第\(n\)项(POJ3070)

\(1\le n\le10^{18}\),对大质数取模

\(f_0=0,f_1=1,f_n=f_{n-1}+f_{n-2}\)

我们设矩阵\(A_n=\left[\begin{matrix}f_n,f_{n-1}\end{matrix}\right]\)

弄一个矩阵

\[\left[\begin{matrix}0&1\\1&1\end{matrix}\right] \]

\(A_{n-1}\)乘上这玩意就是\(A_n\)

于是我们就可以使用矩阵乘法快速幂。

#include <cstdio>
#include <cstring>

using namespace std;
typedef long long ll;
const int SZ = 2;
const ll P = 10000;

struct Mat
{
    ll a[SZ + 5][SZ + 5];
} ys, ans;

inline Mat mul(Mat a, Mat b)
{
    Mat res;
    ll r;
    memset(res.a, 0, sizeof(res.a));
    for (int i = 1; i <= SZ; i++)
        for (int k = 1; k <= SZ; k++)
        {
            r = a.a[i][k];
            for (int j = 1; j <= SZ; j++)
                (res.a[i][j] += b.a[k][j] * r) %= P;
        }
    return res;
}

inline void qpow(ll p)
{
    for (; p; p >>= 1, ys = mul(ys, ys))
        if (p & 1) ans = mul(ans, ys);
}

void init()
{
    memset(ans.a, 0, sizeof(ans.a));
    for (int i = 1; i <= SZ; i++) ans.a[i][i] = 1;
    memset(ys.a, 0, sizeof(ys.a));
    ys.a[1][2] = ys.a[2][1] = ys.a[2][2] = 1ll;
}

int main()
{
    ll n;
    for (; scanf("%lld", &n), ~n;)
        if (n <= 1) printf("%lld\n", n);
        else init(), qpow(n + 1), printf("%lld\n", ans.a[1][1]);
    return 0;
}

Luogu P5110 块速递推

给出数列\(a\),满足\(a_0=0,a_1=1,a_n=233a_{n-1}+666a_{n-2}\),求\(A_n\),对\(10^9+7\)取模

\(n<2^{64},1\le T\le5\times10^7\)

\(T\)组询问,每个询问查询\(A_n\),求所有询问答案的异或和

设矩阵\(\left[\begin{matrix}A_{n-1}&A_n\end{matrix}\right]\)

老套路

\[\left[\begin{matrix}A_{n-1}&A_n\end{matrix}\right]\\=\left[\begin{matrix}A_{n-2}&A_{n-1}\end{matrix}\right]\times\left[\begin{matrix}0&666\\1&233\end{matrix}\right] \]

但是询问数大的一批。

我们可以预处理出到\(\sqrt n\)的答案,询问时只要将\(n\)拆为一个\(ak+b\)的数,直接\(O(1)\)查询。

#pragma GCC optimize("inline,Ofast", 3)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")

#include <bits/stdc++.h>

using namespace std;
typedef unsigned long long llu;
typedef long long ll;
const int N = 32000, P = 1e9 + 7;

struct Mat
{
    int a[2][2];
    inline Mat operator* (const Mat &t) const
    {
        Mat res;
        res.a[0][0] = (1ll * a[0][0]* t.a[0][0] + 1ll * a[0][1] * t.a[1][0]) % P;
        res.a[0][1] = (1ll * a[0][0]* t.a[0][1] + 1ll * a[0][1] * t.a[1][1]) % P;
        res.a[1][0] = (1ll * a[1][0]* t.a[0][0] + 1ll * a[1][1] * t.a[1][0]) % P;
        res.a[1][1] = (1ll * a[1][0]* t.a[0][1] + 1ll * a[1][1] * t.a[1][1]) % P;
        return res;
    }
} m1[N + 5], m2[N + 5], s;
llu SA, SB, SC;

llu Rand()
{
    SA ^= SA << 32, SA ^= SA >> 13, SA ^= SA << 1;
    llu t = SA;
    SA = SB, SB = SC, SC ^= t ^ SA;
    return SC;
}

int main()
{
    s = {233, 666, 1, 0}, m1[0] = m2[0] = {1, 0, 0, 1}; 
    for (int i = 1; i <= N; i++) m1[i] = m1[i - 1] * s;
    s = m1[N];
    for (int i = 1; i <= N; i++) m2[i] = m2[i - 1] * s;
    int T;
    scanf("%d%llu%llu%llu", &T, &SA, &SB, &SC);
    int ans = 0;
    for (int n; T--;)
        n = (Rand() - 1) % (P - 1), ans ^= (m2[n / N] * m1[n % N]).a[0][0];
    return printf("%d\n", ans), 0;
}
```cpp
posted @ 2020-04-23 21:16  Oply  阅读(349)  评论(0编辑  收藏  举报