矩阵快速幂
例行公事先介绍下矩阵乘法~~(相信大家都会)~~
矩阵
- 在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合。
矩阵乘法
- 矩阵相乘只有在第一个矩阵的列数(column)和第二个矩阵的行数(row)相同时才有意义
- 一个m×n的矩阵就是m×n个数排成m行n列的一个数阵
- 由于它把许多数据紧凑的集中到了一起,所以有时候可以简便地表示一些复杂的模型,如电力系统网络模型
设A为
m
∗
p
m*p
m∗p的矩阵,B为
p
∗
n
p*n
p∗n的矩阵,那么称
m
∗
n
m*n
m∗n的矩阵C为矩阵A与B的乘积,记作
C
=
A
B
C = AB
C=AB
其中矩阵C中的第 i 行第 j 列元素可以表示为:
C
[
i
]
[
j
]
=
∑
k
=
1
p
a
[
i
]
[
k
]
∗
b
[
k
]
[
j
]
=
a
[
i
]
[
1
]
∗
b
[
1
]
[
j
]
+
a
[
i
]
[
2
]
∗
b
[
2
]
[
j
]
+
⋯
+
a
[
i
]
[
p
]
∗
b
[
p
]
[
j
]
C[i][j]=\sum_{k=1}^{p}{a[i][k]*b[k][j]}=a[i][1]*b[1][j]+a[i][2]*b[2][j]+\cdots+a[i][p]*b[p][j]
C[i][j]=k=1∑pa[i][k]∗b[k][j]=a[i][1]∗b[1][j]+a[i][2]∗b[2][j]+⋯+a[i][p]∗b[p][j]
- 对于矩阵 C = A B C = AB C=AB ,矩阵A的第m行“乘上”矩阵B的第n列
精简板子
struct Matrix {
LL n, m;
LL v[maxn][maxn];
Matrix(int n = 0, int m = 0) :n(n), m(m) { //构造函数
memset(v, 0, sizeof(v));
}
void clear() { //重置函数
memset(v, 0, sizeof(v));
}
void show() { //debug函数
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
cout << v[i][j] << ' ';
cout << '\n';
}
}
friend Matrix operator *(const Matrix& a, const Matrix& b) {//乘法封装
Matrix res(a.n, b.m);
for (int i = 1; i <= a.n; i++)
for (int j = 1; j <= b.m; j++)
for (int k = 1; k <= a.m; k++)
res.v[i][j] = (a.v[i][k] * b.v[k][j] % mod + res.v[i][j]) % mod;
return res;
}
};
矩阵快速幂
- 所谓矩阵快速幂,即为使用快速幂的计算方式去计算相同矩阵连乘(即矩阵的幂)的结果
- 前面已经讲过,可以将矩阵构造成结构体,重载运算符后就可以直接当做正常变量来使用了
void quickpow(Matrix& res, Matrix m, LL p) {
while (p) {
if (p & 1)
res = res * m;
m = m * m;
p >>= 1;
}
}
重点
以下为矩阵快速幂,常见的题目整理和公式
Fib数
( F i b ( 1 ) F i b ( 2 ) ) ∗ ( 0 1 1 1 ) \left(\begin{array}{ccc}Fib(1)&Fib(2)\\\end{array}\right)*\left(\begin{array}{ccc}0&1\\1& 1\\\end{array}\right) (Fib(1)Fib(2))∗(0111) = = = ( F i b ( 2 ) F i b ( 1 ) + F i b ( 2 ) ) \left(\begin{array}{ccc} Fib(2)&Fib(1)+Fib(2)\end{array}\right) (Fib(2)Fib(1)+Fib(2))
P3390 【模板】矩阵快速幂模板题
常用推导
HDU 5015 233 Matrix
题意
给出矩阵的第0行 ( 233 , 2333 , 23333 , ⋯   ) (233,2333,23333,\cdots) (233,2333,23333,⋯)和第0列 a 1 , a 2 , ⋯   , a n a1,a2,\cdots,an a1,a2,⋯,an ( n < = 10 , m < = 1 0 9 ) (n<=10,m<=10^9) (n<=10,m<=109),给出式子: A [ i ] [ j ] = A [ i − 1 ] [ j ] + A [ i ] [ j − 1 ] A[i][j] = A[i-1][j] + A[i][j-1] A[i][j]=A[i−1][j]+A[i][j−1],要求 A [ n ] [ m ] A[n][m] A[n][m]
思路
较为复杂的一个推导
- 可以假设未给出的第0行第0列值为23
- 对于每一列,可以将所有元素一起构造成初始矩阵。
CF392C Yet Another Number Sequence
题意
给定
n
,
k
,
F
i
b
(
1
)
=
1
,
F
i
b
(
2
)
=
2
n,k,Fib(1)=1,Fib(2)=2
n,k,Fib(1)=1,Fib(2)=2,令
A
k
(
i
)
=
F
i
b
(
i
)
∗
i
k
A_{k}(i)=Fib(i)*i^k
Ak(i)=Fib(i)∗ik
求
∑
i
=
1
n
A
k
(
i
)
\sum_{i=1}^{n}A_{k}(i)
∑i=1nAk(i)
(
k
≤
40
)
(k\leq40)
(k≤40)
思路
- 本题的难点在于 i k i^k ik的推导
- 以下表格展示使用二项式定理推导 i 3 i^3 i3的过程
1 0 1^0 10 | 1 1 1^1 11 | 1 2 1^2 12 |
---|---|---|
C [ 0 ] [ 0 ] ∗ 1 0 = 2 0 C[0][0]*1^0=2^0 C[0][0]∗10=20 | C [ 1 ] [ 0 ] ∗ 1 0 + C [ 1 ] [ 1 ] ∗ 1 1 = 2 1 C[1][0]*1^0+C[1][1]*1^1=2^1 C[1][0]∗10+C[1][1]∗11=21 | C [ 2 ] [ 0 ] ∗ 1 0 + C [ 2 ] [ 1 ] ∗ 1 1 + C [ 2 ] [ 2 ] ∗ 1 2 = 2 2 C[2][0]*1^0+C[2][1]*1^1+C[2][2]*1^2=2^2 C[2][0]∗10+C[2][1]∗11+C[2][2]∗12=22 |
C [ 0 ] [ 0 ] ∗ 2 0 = 3 0 C[0][0]*2^0=3^0 C[0][0]∗20=30 | C [ 1 ] [ 0 ] ∗ 2 0 + C [ 1 ] [ 1 ] ∗ 2 1 = 3 1 C[1][0]*2^0+C[1][1]*2^1=3^1 C[1][0]∗20+C[1][1]∗21=31 | C [ 2 ] [ 0 ] ∗ 2 0 + C [ 2 ] [ 1 ] ∗ 2 1 + C [ 2 ] [ 2 ] ∗ 2 2 = 3 2 C[2][0]*2^0+C[2][1]*2^1+C[2][2]*2^2=3^2 C[2][0]∗20+C[2][1]∗21+C[2][2]∗22=32 |
HDU4686 Arc of Dream
题意
a
i
=
a
i
−
1
∗
a
x
+
a
y
a_{i}=a_{i-1}*ax+ay
ai=ai−1∗ax+ay
b
i
=
b
i
−
1
∗
b
x
+
b
y
b_{i}=b_{i-1}*bx+by
bi=bi−1∗bx+by
求
∑
i
=
0
i
<
n
a
i
∗
b
i
\sum_{i=0}^{i < n} a_{i}*b_{i}
∑i=0i<nai∗bi
思路
本题难度在于
a
i
∗
b
i
a_{i}*b_{i}
ai∗bi的推导
显然由
a
i
a_{i}
ai和
b
i
b_{i}
bi使用矩阵快速幂推导比较困难
但是由
a
i
∗
b
i
,
a
i
,
b
i
a_{i}*b_{i},a_{i},b_{i}
ai∗bi,ai,bi推导就可以了
(
a
i
∗
a
x
+
a
y
)
∗
(
b
i
∗
b
x
+
b
y
)
=
a
x
∗
b
x
∗
a
i
∗
b
i
+
b
x
∗
a
y
∗
b
i
+
a
x
∗
b
y
∗
a
i
+
a
y
∗
b
y
(a_{i}*ax+ay)*(b_{i}*bx+by)=ax*bx*a_{i}*b_{i}+bx*ay*b_{i}+ax*by*a_{i}+ay*by
(ai∗ax+ay)∗(bi∗bx+by)=ax∗bx∗ai∗bi+bx∗ay∗bi+ax∗by∗ai+ay∗by
以
(
a
i
∗
b
i
,
a
i
,
a
y
,
b
i
,
b
y
,
s
u
m
)
(a_{i}*b_{i},a_{i},ay,b_{i},by,sum)
(ai∗bi,ai,ay,bi,by,sum)为初始矩阵推导即可
CF385 E Bear in the Field
题意
有一片
n
×
n
n×n
n×n的草莓地,每个位置的初始草莓量为横坐标和纵坐标的和,然后每过一秒增长一个草莓
然后给出熊的初始位置
(
s
x
,
s
y
)
(sx,sy)
(sx,sy),以及移动的速度
(
d
x
,
d
y
)
(dx,dy)
(dx,dy),每一秒发生的事:
- 速度增加k(k为该位置的草莓数)
- 熊的位置发生移动
- 每个位置上草莓数+1
求t秒后熊的位置
思路
为了方便求余n,将坐标由
[
1
,
n
]
[1,n]
[1,n]变成
[
0
,
n
−
1
]
[0,n-1]
[0,n−1]
233 Matrix 代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 13;
const LL mod = 10000007;
struct Matrix
{
int n, m;
int v[maxn][maxn];
Matrix(int n = 0, int m = 0):n(n),m(m) {
memset(v, 0, sizeof(v));
}
void clear() {
memset(v, 0, sizeof(v));
}
void show() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
cout << v[i][j] << ' ';
cout << '\n';
}
}
friend Matrix operator *(const Matrix& a, const Matrix& b) {
Matrix res(a.n, b.m);
for (int i = 1; i <= a.n; i++)
for (int j = 1; j <= b.m; j++)
for (int k = 1; k <= a.m; k++)
res.v[i][j] = (res.v[i][j] + LL(a.v[i][k]) * b.v[k][j] % mod) % mod;
return res;
}
};
void quickpow(Matrix &res, Matrix m, LL p) {
while (p) {
if (p & 1)
res = res * m;
m = m * m;
p >>= 1;
}
}
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
Matrix a(1, n + 2), b(n + 2, n + 2);
a.v[1][1] = 23;
for (int i = 2; i <= n + 1; i++)
scanf("%d", &a.v[1][i]);
a.v[1][n + 2] = 3;
for (int i = 1; i <= n + 2; i++)
b.v[1][i] = 10, b.v[n + 2][i] = 1;
b.v[1][n + 2] = 0;
for (int i = 2; i <= n + 1; i++)
for (int j = i; j <= n + 1; j++)
b.v[i][j] = 1;
quickpow(a, b, m);
printf("%d\n", a.v[1][n + 1]);
}
}
Yet Another Number Sequence 代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 85;
const LL mod = 1e9 + 7;
LL n; int k;
struct Matrix
{
LL n, m;
LL v[maxn][maxn];
Matrix(int n = 0, int m = 0) :n(n), m(m) {
memset(v, 0, sizeof(v));
}
void clear() {
memset(v, 0, sizeof(v));
}
void show() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
cout << v[i][j] << ' ';
cout << '\n';
}
}
friend Matrix operator *(const Matrix& a, const Matrix& b) {
Matrix res(a.n, b.m);
for (int i = 1; i <= a.n; i++)
for (int j = 1; j <= b.m; j++)
for (int k = 1; k <= a.m; k++)
res.v[i][j] = (res.v[i][j] + a.v[i][k] * b.v[k][j] % mod) % mod;
return res;
}
};
int comb[41][41];
void Pre_Work() {
for (int i = 0; i <= 40; i++)
comb[i][i] = comb[i][0] = 1;
for (int i = 2; i <= 40; i++) {
for (int j = 1; j < i; j++)
comb[i][j] = (LL(comb[i - 1][j]) + comb[i - 1][j - 1]) % mod;
}
}
Matrix a;
void Matrix_a() {
a.n = 1; a.m = 2 * k + 3;
for (int i = 1; i <= k + 1; i++)
a.v[1][i] = (LL(1) << (i - 1)) % mod;
for (int i = k + 2; i <= 2 * k + 2; i++)
a.v[1][i] = (LL(2) << (i - k - 2)) % mod;
a.v[1][2 * k + 3] = (LL(1) << k + 1) + 1;
//a.show();
}
Matrix b;
void Matrix_b() {
b.n = 2 * k + 3;b.m = 2 * k + 3;
for (int i = 1; i <= k + 1; i++)
for (int j = 1; j <= i; j++)
b.v[j + k + 1][i] = comb[i - 1][j - 1];
for (int i = k + 2; i <= 2 * k + 2; i++) {
for (int j = 1; j <= i - k - 1; j++) {
b.v[j][i] = comb[i - k - 2][j - 1];
b.v[j + k + 1][i] = comb[i - k - 2][j - 1];
}
}
for (int i = 1; i <= k + 1; i++)
b.v[i][2 * k + 3] = b.v[i + k + 1][2 * k + 3] = comb[k][i - 1];
b.v[2 * k + 3][2 * k + 3] = 1;
//b.show();
}
void quickpow(Matrix& res, Matrix m, LL p) {
while (p) {
if (p & 1)
res = res * m;
m = m * m;
p >>= 1;
}
}
int main() {
scanf("%I64d%d", &n, &k);
if (n == 1) {
printf("1\n");
return 0;
}
Pre_Work();
Matrix_a();
Matrix_b();
quickpow(a, b, n - 2);
printf("%I64d\n", a.v[1][2 * k + 3]);
}
C - Arc of Dream 代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const LL mod = 1e9 + 7;
const int maxn = 7;
struct Matrix
{
LL n, m;
LL v[maxn][maxn];
Matrix(int n = 0, int m = 0) :n(n), m(m) {
memset(v, 0, sizeof(v));
}
void clear() {
memset(v, 0, sizeof(v));
}
void show() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
cout << v[i][j] << ' ';
cout << '\n';
}
}
friend Matrix operator *(const Matrix& a, const Matrix& b) {
Matrix res(a.n, b.m);
for (int i = 1; i <= a.n; i++)
for (int j = 1; j <= b.m; j++)
for (int k = 1; k <= a.m; k++)
res.v[i][j] = (a.v[i][k] * b.v[k][j] % mod + res.v[i][j])%mod;
return res;
}
};
LL a0, ax, ay, b0, bx, by;
Matrix a;
void Matrix_a() {
a.clear();
a.n = 1; a.m = 6;
a.v[1][1] = a0 * b0; a.v[1][1] %= mod;
a.v[1][2] = a0;
a.v[1][3] = ay;
a.v[1][4] = b0;
a.v[1][5] = by;
a.v[1][6] = 0;
//a.show();
}
Matrix b;
void Matrix_b() {
b.clear();
b.n = 6; b.m = 6;
b.v[1][1] = ax * bx; b.v[1][1] %= mod;
b.v[2][1] = ax * by; b.v[2][1] %= mod;
b.v[3][1] = by;
b.v[4][1] = bx * ay; b.v[4][1] %= mod;
b.v[2][2] = ax;
b.v[3][2] = 1;
b.v[3][3] = 1;
b.v[4][4] = bx;
b.v[5][4] = 1;
b.v[5][5] = 1;
b.v[1][6] = b.v[6][6] = 1;
//b.show();
}
void quickpow(Matrix& res, Matrix m, LL p) {
while (p) {
if (p & 1)
res = res * m;
m = m * m;
p >>= 1;
}
}
int main() {
LL n;
while (~scanf("%lld", &n)) {
scanf("%lld%lld%lld", &a0, &ax, &ay);
a0 %= mod; ax %= mod; ay %= mod;
scanf("%lld%lld%lld", &b0, &bx, &by);
b0 %= mod; bx %= mod; by %= mod;
Matrix_a();
Matrix_b();
quickpow(a, b, n);
printf("%lld\n", a.v[1][6]);
}
}
Bear in the Field 代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#pragma warning (disable:4996)
typedef long long LL;
const int maxn = 7;
LL mod;
struct Matrix
{
LL n, m;
LL v[maxn][maxn];
Matrix(int n = 0, int m = 0) :n(n), m(m) {
memset(v, 0, sizeof(v));
}
void clear() {
memset(v, 0, sizeof(v));
}
void show() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
cout << v[i][j] << ' ';
cout << '\n';
}
}
friend Matrix operator *(const Matrix& a, const Matrix& b) {
Matrix res(a.n, b.m);
for (int i = 1; i <= a.n; i++)
for (int j = 1; j <= b.m; j++)
for (int k = 1; k <= a.m; k++)
res.v[i][j] = (a.v[i][k] * b.v[k][j] % mod + res.v[i][j]) % mod;
return res;
}
};
LL sx, sy, dx, dy, n;
Matrix a;
void Matrix_a() {
a.clear();
a.n = 1; a.m = 6;
a.v[1][1] = sx;
a.v[1][2] = sy;
a.v[1][3] = dx;
a.v[1][4] = dy;
a.v[1][5] = 0;
a.v[1][6] = 1;
//a.show();
}
Matrix b;
void Matrix_b() {
b.clear();
b.n = 6; b.m = 6;
b.v[1][1] = b.v[6][1] = 2;
b.v[2][1] = b.v[3][1] = b.v[5][1] = 1;
b.v[2][2] = b.v[6][2] = 2;
b.v[1][2] = b.v[4][2] = b.v[5][2] = 1;
b.v[6][3] = 2;
b.v[1][3] = b.v[2][3] = b.v[3][3] = b.v[5][3] = 1;
b.v[6][4] = 2;
b.v[1][4] = b.v[2][4] = b.v[4][4] = b.v[5][4] = 1;
b.v[5][5] = b.v[6][5] = 1;
b.v[6][6] = 1;
//b.show();
}
void quickpow(Matrix& res, Matrix& m, LL p) {
while (p) {
if (p & 1)
res = res * m;
m = m * m;
p >>= 1;
}
}
int main() {
scanf("%I64d", &mod);
scanf("%I64d%I64d%I64d%I64d%I64d", &sx, &sy, &dx, &dy, &n);
sx--; sy--;
Matrix_a();
Matrix_b();
quickpow(a, b, n);
a.v[1][1]++; a.v[1][2]++;
if (a.v[1][1] <= 0)a.v[1][1] += mod;
if (a.v[1][2] <= 0)a.v[1][2] += mod;
printf("%I64d %I64d\n", a.v[1][1], a.v[1][2]);
}