矩阵
定义
只有\(A的行数\)和\(B的列数\)相等,这两个矩阵才可以相乘
设\(A\)是一个\(n\times p\)的矩阵,\(B\)是一个\(p\times m\)的矩阵。
若\(A\times B=C\),那么\(C\)肯定是一个\(n\times m\)的矩阵。
矩阵\(C\)中的元素可以表示为
举个栗子:
国家级冠军的必备千位数加减!
矩阵乘法不满足交换律(不用说都知道),但是满足结合律。
也很好证明。
若\(A\times B\times C=D\)
设\(A\times B=T\)
设\(A\times (B\times C)=E\)
同理得
可得\(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]\)
弄一个矩阵
\(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]\)
老套路
但是询问数大的一批。
我们可以预处理出到\(\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