一切递推皆可矩阵乘法,一切DP皆可背包!
学过线性代数的人肯定都知道矩阵乘法,在acm中矩阵乘法往往是用来快速求递推式的第n项,而且经常是与快速幂联系在一起使用。
下面举几个例子,简单题就不贴代码了:
Hdu1575(Tr A):
题意:Tr A表示方阵A的迹(主对角线元素之和),求Tr(Ak) % 9973。
算法:直接用矩阵快速幂。
Poj3233(Matrix Power Series):
题意:求Sn = A + A2 +A3 + … +An。
算法:首先能矩阵二分快速幂计算出Ak,那么我们对S再进行二分:
Hdu2604(Queuing):
题意:求长度为L的合法串,非法只包含fmf,fff
算法:画DFA,求出递推式,并构造矩阵:
Hdu2294(Pendant):
题意:题意求长为n的挂件一定包含1~k种颜色的方案数。
算法:dp[i][j]表示用j种颜色,组成长度为i的方案数,那么dp[i][j] = j*dp[i-1][j] + (k-j+1)*dp[i-1][j-1]。最后答案即为∑dp[i][k]。
因为n非常大,并且每个状态只跟前一个有关,所以可以用滚动数组降到一维,对于每一个长度i,构造(k+1)*(k+1)矩阵:
dp[0] dp[1] ...... dp[k]
0 0 ...... 0
... ... ...... ...
0 0 ...... 0
可知f[n]=A*f[n-1]=A^(n-1)*f[1],所以最后要求的答案即为(E+A+A^2+......+A^(n-1))*f[1]。(f[i]是上面那个矩阵,dp[i]是这个矩阵中的一个数值)
由递推式构造出A矩阵为:
0 k 0 ...... 0 0
0 1 k-1 ...... 0 0
0 0 2 ...... 0 0
... ... ... ...... ...
0 0 0 ...... k-1 1
0 0 0 ...... 0 k
并且f[1]=E*A。当然,在计算过程中还需要取模。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
#include <cstdio>
#include <cstring>
const
long
long
mod
=
1234567891
;
int
N
,
K
;
struct
Matrix
{
int
n
,
m
;
long
long
mat
[
33
]
[
33
]
;
}
A
,
F
,
I
;
void
initF
(
)
{
F
.
n
=
F
.
m
=
K
+
1
;
for
(
int
i
=
0
;
i
<
F
.
n
;
i
++
)
for
(
int
j
=
0
;
j
<
F
.
m
;
j
++
)
F
.
mat
[
i
]
[
j
]
=
0
;
}
void
initI
(
)
{
I
.
n
=
I
.
m
=
K
+
1
;
for
(
int
i
=
0
;
i
<
I
.
n
;
i
++
)
for
(
int
j
=
0
;
j
<
I
.
m
;
j
++
)
I
.
mat
[
i
]
[
j
]
=
0
;
for
(
int
i
=
0
;
i
<
I
.
n
;
i
++
)
I
.
mat
[
i
]
[
i
]
=
1
;
}
void
initA
(
)
{
A
.
n
=
A
.
m
=
K
+
1
;
for
(
int
i
=
0
;
i
<
A
.
n
;
i
++
)
for
(
int
j
=
0
;
j
<
A
.
m
;
j
++
)
A
.
mat
[
i
]
[
j
]
=
0
;
for
(
int
i
=
0
;
i
<
A
.
n
;
i
++
)
{
A
.
mat
[
i
]
[
i
]
=
i
;
if
(
i
<
A
.
n
-
1
)
A
.
mat
[
i
]
[
i
+
1
]
=
K
-
i
;
}
}
Matrix
operator*
(
Matrix
a
,
Matrix
b
)
{
Matrix
c
;
c
.
n
=
a
.
n
;
c
.
m
=
b
.
m
;
for
(
int
i
=
0
;
i
<
c
.
n
;
i
++
)
for
(
int
j
=
0
;
j
<
c
.
m
;
j
++
)
{
c
.
mat
[
i
]
[
j
]
=
0
;
for
(
int
k
=
0
;
k
<
a
.
m
;
k
++
)
c
.
mat
[
i
]
[
j
]
=
(
c
.
mat
[
i
]
[
j
]
+
a
.
mat
[
i
]
[
k
]
*
b
.
mat
[
k
]
[
j
]
)
%
mod
;
}
return
c
;
}
Matrix
operator
+
(
Matrix
a
,
Matrix
b
)
{
Matrix
c
;
c
.
n
=
a
.
n
;
c
.
m
=
b
.
m
;
for
(
int
i
=
0
;
i
<
c
.
n
;
i
++
)
for
(
int
j
=
0
;
j
<
c
.
m
;
j
++
)
c
.
mat
[
i
]
[
j
]
=
(
a
.
mat
[
i
]
[
j
]
+
b
.
mat
[
i
]
[
j
]
)
%
mod
;
return
c
;
}
Matrix
operator
^
(
Matrix
A
,
int
x
)
{
Matrix
c
;
c
.
n
=
c
.
m
=
K
+
1
;
c
=
I
;
for
(
;
x
;
x
>>
=
1
)
{
|