数学知识——矩阵乘法
使用矩阵快速幂优化递推问题
对于一个递推问题,如递推式的每一项系数都为常数,我们可以使用矩阵快速幂来对算法进行优化。
一般形式为:
F
n
=
F
1
×
A
n
−
1
F_n=F_1×A^{n-1}
Fn=F1×An−1
由于递推式的每一项系数都为常数,因此对于每一步的递推,
A
A
A里面都是相同的常数。
对于
A
n
−
1
A^{n-1}
An−1部分使用快速幂求解,根据
F
n
F_n
Fn就能得到最终答案。
矩阵快速幂:
void mul(int c[][N], int a[][N], int b[][N])
{
int temp[N][N] = {0};
for (int i = 0; i < N; i ++ )
for (int j = 0; j < N; j ++ )
for (int k = 0; k < N; k ++ )
temp[i][j] = (temp[i][j] + (ll)a[i][k] * b[k][j] % m) % m;
memcpy(c, temp, sizeof temp);
}
while (k)
{
if (k & 1) mul(f, f, a);
mul(a, a, a);
k >>= 1;
}
斐波那契数列的前n项和
大家都知道 Fibonacci 数列吧,
f
1
=
1
,
f
2
=
1
,
f
3
=
2
,
f
4
=
3
,
…
,
f
n
=
f
n
−
1
+
f
n
−
2
f_1=1,f_2=1,f_3=2,f_4=3,…,f_n=f_{n−1}+f_{n−2}
f1=1,f2=1,f3=2,f4=3,…,fn=fn−1+fn−2。
现在问题很简单,输入
n
n
n 和
m
m
m,求
f
n
f_n
fn的前
n
n
n 项和
S
n
(
m
o
d
m
)
S_n(mod\ m)
Sn(mod m)。
输入格式
共一行,包含两个整数 n n n 和 m m m。
输出格式
输出前 n n n 项和 S n ( m o d m ) S_n (mod\ m) Sn(mod m) 的值。
数据范围
1
≤
n
≤
2000000000
,
1≤n≤2000000000,
1≤n≤2000000000,
1
≤
m
≤
1000000010
1≤m≤1000000010
1≤m≤1000000010
输入样例:
5 1000
输出样例:
12
我们有递推关系:
f
n
=
f
n
−
1
+
f
n
−
2
f_n=f_{n-1}+f_{n-2}
fn=fn−1+fn−2
S
n
=
S
n
−
1
+
f
n
S_n=S_{n-1}+f_n
Sn=Sn−1+fn
设
F
n
=
[
f
n
,
f
n
+
1
,
S
n
]
F_n=[f_n,f_{n+1},S_n]
Fn=[fn,fn+1,Sn],有
F
n
×
A
=
F
n
+
1
F_n×A=F_{n+1}
Fn×A=Fn+1,根据递推关系求解矩阵
A
A
A。
[
f
n
,
f
n
+
1
,
S
n
]
×
[
a
11
a
12
a
13
a
21
a
22
a
23
a
31
a
32
a
33
]
=
[
f
n
+
1
,
f
n
+
2
,
S
n
+
1
]
[f_n,f_{n+1},S_n]× \begin{bmatrix} a_{11} & a_{12}& a_{13}\\ a_{21}& a_{22}& a_{23}\\ a_{31} & a_{32} & a_{33} \end{bmatrix}=[f_{n+1},f_{n+2},S_{n+1}]
[fn,fn+1,Sn]×
a11a21a31a12a22a32a13a23a33
=[fn+1,fn+2,Sn+1]
求解得到 A = [ 0 1 0 1 1 1 0 0 1 ] A = \begin{bmatrix} 0 & 1 & 0 \\ 1 & 1 & 1 \\ 0 & 0 & 1 \end{bmatrix} A= 010110011
F 1 = [ 1 , 1 , 1 ] F_1=[1,1,1] F1=[1,1,1],最终答案为 F 1 × A n − 1 F_1×A^{n-1} F1×An−1,对于 A n − 1 A^{n-1} An−1部分采用快速幂来计算,时间复杂度为 O ( l o g n ∗ 3 3 ) O(logn*3^3) O(logn∗33)。
佳佳的斐波那契
佳佳对数学,尤其对数列十分感兴趣。
在研究完 Fibonacci 数列后,他创造出许多稀奇古怪的数列。
例如用 S ( n ) S(n) S(n) 表示 Fibonacci 前 n n n 项和 m o d m mod\ m mod m 的值,即 S ( n ) = ( F 1 + F 2 + … + F n ) m o d m S(n)=(F_1+F_2+…+F_n)mod\ m S(n)=(F1+F2+…+Fn)mod m,其中 F 1 = F 2 = 1 , F i = F i − 1 + F i − 2 F_1=F_2=1,F_i=F_{i−1}+F_{i−2} F1=F2=1,Fi=Fi−1+Fi−2。
可这对佳佳来说还是小菜一碟。
终于,她找到了一个自己解决不了的问题。
用 T ( n ) = ( F 1 + 2 F 2 + 3 F 3 + … + n F n ) m o d m T(n)=(F_1+2F_2+3F_3+…+nF_n)mod\ m T(n)=(F1+2F2+3F3+…+nFn)mod m 表示 Fibonacci 数列前 n n n 项变形后的和 m o d m mod\ m mod m 的值。
现在佳佳告诉你了一个 n n n 和 m m m,请求出 T ( n ) T(n) T(n)的值。
输入格式
共一行,包含两个整数 n n n 和 m m m。
输出格式
共一行,输出 T ( n ) T(n) T(n) 的值。
数据范围
1
≤
n
,
m
≤
2
31
−
1
1≤n,m≤2^{31−1}
1≤n,m≤231−1
输入样例:
5 5
输出样例:
1
样例解释
T
(
5
)
=
(
1
+
2
×
1
+
3
×
2
+
4
×
3
+
5
×
5
)
m
o
d
5
=
1
T(5)=(1+2×1+3×2+4×3+5×5)mod\ 5=1
T(5)=(1+2×1+3×2+4×3+5×5)mod 5=1
我们有递推关系:
f
n
=
f
n
−
1
+
f
n
−
2
f_n=f_{n-1}+f_{n-2}
fn=fn−1+fn−2
S
n
=
S
n
−
1
+
f
n
S_n=S_{n-1}+f_n
Sn=Sn−1+fn
T
n
=
T
n
−
1
+
n
∗
f
n
T_n=T_{n-1}+n*f_n
Tn=Tn−1+n∗fn
但是对于
T
n
=
T
n
−
1
+
n
∗
f
n
T_n=T_{n-1}+n*f_n
Tn=Tn−1+n∗fn,
f
n
f_n
fn的系数不为常数,所以无所用矩阵快速幂来优化。
令
P
n
=
n
S
n
−
T
n
P_n=nS_n-T_n
Pn=nSn−Tn,那么有:
P
n
=
(
n
−
1
)
S
1
+
(
n
−
2
)
S
2
.
.
.
+
S
n
−
1
P_n=(n-1)S_1+(n-2)S_2...+S_{n-1}
Pn=(n−1)S1+(n−2)S2...+Sn−1,
P
n
+
1
=
n
S
1
+
(
n
−
1
)
S
2
.
.
.
+
2
S
n
−
1
+
S
n
P_{n+1}=nS_1+(n-1)S_2...+2S_{n-1}+S_{n}
Pn+1=nS1+(n−1)S2...+2Sn−1+Sn,
P
n
+
1
−
P
n
=
S
n
P_{n+1}-P_n=S_n
Pn+1−Pn=Sn
于是,我们有以下三组递推关系:
f
n
=
f
n
−
1
+
f
n
−
2
f_n=f_{n-1}+f_{n-2}
fn=fn−1+fn−2
S
n
=
S
n
−
1
+
f
n
S_n=S_{n-1}+f_n
Sn=Sn−1+fn
P
n
=
P
n
−
1
+
S
n
−
1
P_n=P_{n-1}+S_{n-1}
Pn=Pn−1+Sn−1
每一组的系数都为常数,我们可以使用矩阵快速幂来优化。
设
F
n
=
[
f
n
,
f
n
+
1
,
S
n
,
P
n
]
F_n=[f_n,f_{n+1},S_n,P_n]
Fn=[fn,fn+1,Sn,Pn],有
F
n
×
A
=
F
n
+
1
F_n×A=F_{n+1}
Fn×A=Fn+1,根据递推关系求解矩阵
A
A
A。
[
f
n
,
f
n
+
1
,
S
n
,
P
n
]
×
[
a
11
a
12
a
13
a
14
a
21
a
22
a
23
a
24
a
31
a
32
a
33
a
34
a
41
a
42
a
43
a
44
]
=
[
f
n
+
1
,
f
n
+
2
,
S
n
+
1
,
P
n
+
1
]
[f_n,f_{n+1},S_n,P_n]× \begin{bmatrix} a_{11} & a_{12}& a_{13}& a_{14}\\ a_{21}& a_{22}& a_{23}& a_{24}\\ a_{31} & a_{32} & a_{33}& a_{34}\\ a_{41} & a_{42} & a_{43}& a_{44}\\ \end{bmatrix}=[f_{n+1},f_{n+2},S_{n+1},P_{n+1}]
[fn,fn+1,Sn,Pn]×
a11a21a31a41a12a22a32a42a13a23a33a43a14a24a34a44
=[fn+1,fn+2,Sn+1,Pn+1]
根据递推关系,解出
A
=
[
0
1
0
0
1
1
1
0
0
0
1
1
0
0
0
1
]
A = \begin{bmatrix} 0 & 1 & 0 & 0\\ 1 & 1 & 1 & 0\\ 0 & 0 & 1 & 1\\ 0 & 0 & 0 & 1 \end{bmatrix}
A=
0100110001100011
F
1
=
[
1
,
1
,
1
,
0
]
,
F_1=[1,1,1,0],
F1=[1,1,1,0],
F
n
=
F
1
×
A
n
−
1
F_n=F_1×A^{n-1}
Fn=F1×An−1
解出来
S
n
S_n
Sn和
P
n
P_n
Pn,最终答案
T
n
=
n
S
n
−
P
n
T_n=nS_n-P_n
Tn=nSn−Pn。
时间复杂度为
O
(
l
o
g
n
∗
4
3
)
O(logn*4^3)
O(logn∗43)。
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 4;
int n, m;
void mul(int a[][N], int b[][N], int c[][N])
{
int temp[N][N] = {0};
for (int i = 0; i < N; i ++ )
for (int j = 0; j < N; j ++ )
for (int k = 0; k < N; k ++ )
temp[i][j] = (temp[i][j] + (ll)b[i][k] * c[k][j] % m) % m;
memcpy(a, temp, sizeof temp);
}
int main()
{
cin >> n >> m;
int f[N][N] = {1, 1, 1, 0};
int a[N][N] = {
{0, 1, 0, 0},
{1, 1, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1}
};
int k = n - 1;
while (k)
{
if (k & 1) mul(f, f, a);
mul(a, a, a);
k >>= 1;
}
cout << ((ll)n * f[0][2]% m - f[0][3] + m) % m << endl;
return 0;
}