矩阵乘法
对于一个
n
×
m
n\times m
n×m 的矩阵
A
A
A 和一个
m
×
k
m\times k
m×k 的矩阵
B
B
B,有且仅有一个
n
×
k
n\times k
n×k 的矩阵
C
C
C 使
A
×
B
=
C
A\times B=C
A×B=C且对于
∀
i
∈
[
1
,
n
]
&
∀
j
∈
[
1
,
k
]
\forall i\in [1,n]\ \&\ \forall j\in [1,k]
∀i∈[1,n] & ∀j∈[1,k],有
C
i
,
j
=
∑
k
=
1
m
A
i
k
×
B
k
j
C_{i,j}=\sum^{m}_{k=1}{A_{ik}\times B_{kj}}
Ci,j=k=1∑mAik×Bkj这就是矩阵乘法。比如:
[
1
2
3
3
2
1
]
×
[
1
1
2
2
3
3
]
=
[
14
14
10
10
]
\left[\begin{array}{ccc}1&2&3\\3&2&1\\\end{array}\right]\times\left[\begin{array}{ccc}1&1\\2&2\\3&3\\\end{array}\right]=\left[\begin{array}{ccc}14&14\\10&10\\\end{array}\right]
[132231]×⎣⎡123123⎦⎤=[14101410]
题目描述 loj10220 \text{loj10220} loj10220
大家都知道 Fibonacci 数列吧, f n = f n − 1 + f n − 2 f_n=f_{n-1}+f_{n-2} fn=fn−1+fn−2。
现在问题很简单,输入 n n n 和 m m m,求 f n m o d    m f_n\mod m fnmodm。
输入格式
输入 n , m n,m n,m。
输出格式
输出 f n m o d    m f_n\mod m fnmodm。
样例输入
5 1000
样例输出
5
数据范围与提示
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 2 × 1 0 9 , 1 ≤ m ≤ 1 0 9 + 10 1\leq n\leq2\times10^9,1\leq m\leq 10^9+10 1≤n≤2×109,1≤m≤109+10 。
Solution 10220 \text{Solution 10220} Solution 10220
考虑使用矩阵快速幂优化时间复杂度。
若矩阵
A
A
A 使
[
f
i
−
2
f
i
−
1
]
×
A
=
[
f
i
−
1
f
i
]
\left[ \begin{array}{ccc} f_{i-2}&f_{i-1} \end{array} \right] \times A=\left[ \begin{array}{ccc} f_{i-1} & f_i \end{array} \right]
[fi−2fi−1]×A=[fi−1fi]则
[
f
n
f
n
−
1
]
=
[
f
1
f
2
]
×
A
n
−
1
\left[ \begin{array}{ccc} f_n & f_{n-1} \end{array} \right]=\left[ \begin{array}{ccc} f_1&f_2 \end{array} \right]\times A^{n-1}
[fnfn−1]=[f1f2]×An−1而
A
n
−
1
A^{n-1}
An−1可以使用快速幂求出。
下面推导
A
A
A。 由矩阵乘法知
A
A
A 是一个
2
×
2
2\times 2
2×2 的矩阵,且
f
i
−
1
=
A
11
×
f
i
−
2
+
A
21
×
f
i
−
1
f_{i-1}=A_{11}\times f_{i-2}+A_{21}\times f_{i-1}
fi−1=A11×fi−2+A21×fi−1
∴
A
11
=
0
,
A
21
=
1
,
\therefore A_{11}=0,A_{21}=1,
∴A11=0,A21=1,
同理得
A
12
=
1
,
A
22
=
1
,
A_{12}=1,A_{22}=1,
A12=1,A22=1,
∴
A
=
[
0
1
1
1
]
.
\therefore A=\left[ \begin{array}{ccc} 0 & 1\\ 1 & 1 \end{array} \right].
∴A=[0111].
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
int n,m;
struct node{
ll a[3][3];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
void pt(){
printf("%lld",a[1][2]);
}
}s,t,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
int main(){
scanf("%d%d",&n,&m);
s.x=1;s.y=2;s.a[1][1]=s.a[1][2]=1;
t.x=2;t.y=2;t.a[1][1]=0;t.a[1][2]=t.a[2][1]=t.a[2][2]=1;
ans=T(s,QT(t,n-2));
ans.pt();
}
题目描述 loj10221 \text{loj10221} loj10221
大家都知道 Fibonacci 数列吧, f n = f n − 1 + f n − 2 f_n=f_{n-1}+f_{n-2} fn=fn−1+fn−2。
求 s n = ∑ k = 1 n f k m o d    m s_n=\sum^{n}_{k=1}{f_k} \mod m sn=∑k=1nfkmodm的值。
样例输入
5 1000
样例输出
12
数据范围与提示
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 2 × 1 0 9 , 1 ≤ m ≤ 1 0 9 + 10 1\leq n\leq2\times10^9,1\leq m\leq 10^9+10 1≤n≤2×109,1≤m≤109+10 。
Solution 10221 \text{Solution 10221} Solution 10221
对于Fibonacci数列前 n n n 项和 s n s_n sn,显然有 s n = s n − 1 + f n s_n=s_{n-1}+f_n sn=sn−1+fn。类似的,我们可以构造一矩阵 B B B,使 [ f i − 2 f i − 1 s i − 1 ] × B = [ f i − 1 f i s i ] \left[ \begin{array}{ccc} f_{i-2}&f_{i-1}&s_{i-1}\\ \end{array} \right] \times B=\left[ \begin{array}{ccc} f_{i-1}&f_i& s_i\\ \end{array} \right] [fi−2fi−1si−1]×B=[fi−1fisi],就能完成此题。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
int n,m;
struct node{
ll a[4][4];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
void pt(){
printf("%lld",a[1][3]);
}
}s,t,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
int main(){
scanf("%d%d",&n,&m);
s.x=1;s.y=3;s.a[1][1]=s.a[1][2]=1;s.a[1][3]=2;
t.x=3;t.y=3;t.a[1][1]=t.a[3][1]=t.a[3][2]=0;t.a[1][2]=t.a[1][3]=t.a[2][1]=t.a[2][2]=t.a[2][3]=t.a[3][3]=1;;
ans=T(s,QT(t,n-2));
ans.pt();
}
题目描述 loj10222 \text{loj10222} loj10222
大家都知道 Fibonacci 数列吧, f n = f n − 1 + f n − 2 f_n=f_{n-1}+f_{n-2} fn=fn−1+fn−2。
求 T n = ∑ k = 1 n k ⋅ f k m o d    m T_n=\sum^{n}_{k=1}{k·f_k} \mod m Tn=∑k=1nk⋅fkmodm 的值。
样例输入
5 5
样例输出
4
数据范围与提示
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 2 31 − 1 1\leq n,m\leq 2^{31}-1 1≤n,m≤231−1 。
Solution 10222 \text{Solution 10222} Solution 10222
考虑构造一
1
×
4
1\times 4
1×4 矩阵
A
i
=
[
f
i
−
2
f
i
−
1
T
i
−
1
i
−
1
]
A_i=\left[ \begin{array}{ccc} f_{i-2}&f_{i-1}&T_{i-1}&i-1 \end{array} \right]
Ai=[fi−2fi−1Ti−1i−1]
使
A
i
×
B
=
A
i
+
1
A_i\times B=A_{i+1}
Ai×B=Ai+1
其中
B
B
B 是一个
4
×
4
4\times 4
4×4 的矩阵。同上文,我们有
f
i
−
1
=
B
11
⋅
f
i
−
2
+
B
21
⋅
f
i
−
1
+
B
31
⋅
T
i
−
1
+
B
41
⋅
(
i
−
1
)
f_{i-1}=B_{11}·f_{i-2}+B_{21}·f_{i-1}+B_{31}·T_{i-1}+B_{41}·(i-1)
fi−1=B11⋅fi−2+B21⋅fi−1+B31⋅Ti−1+B41⋅(i−1)
f
i
=
B
12
⋅
f
i
−
2
+
B
22
⋅
f
i
−
1
+
B
32
⋅
T
i
−
1
+
B
42
⋅
(
i
−
1
)
f_i=B_{12}·f_{i-2}+B_{22}·f_{i-1}+B_{32}·T_{i-1}+B_{42}·(i-1)
fi=B12⋅fi−2+B22⋅fi−1+B32⋅Ti−1+B42⋅(i−1)
T
i
=
⋅
⋅
⋅
T_i=\ ···
Ti= ⋅⋅⋅
那么,
T
i
T_i
Ti 怎么用形如上式的表达式表示呢?
事实上,我们无法用形如上式的表达式表示
T
i
=
T
i
−
1
+
i
⋅
f
i
T_i=T_{i-1}+i·f_i
Ti=Ti−1+i⋅fi,因为我们无法用方阵
B
B
B 的若干次幂的形式表示
i
i
i。
考虑如何将
T
i
T_i
Ti 表示成一次递推式。
(1)
T
n
=
∑
k
=
1
n
k
⋅
f
k
T_n=\sum^{n}_{k=1}{k·f_k}\tag 1
Tn=k=1∑nk⋅fk(1)
(2)
n
s
n
=
∑
k
=
1
n
n
⋅
f
k
ns_n=\sum^{n}_{k=1}{n·f_k}\tag 2
nsn=k=1∑nn⋅fk(2)
(
2
)
−
(
1
)
(2)-(1)
(2)−(1) 得
(3)
n
s
n
−
T
n
=
∑
k
=
1
n
(
n
−
k
)
⋅
f
k
ns_n-T_n=\sum^{n}_{k=1}(n-k)·f_k\tag3
nsn−Tn=k=1∑n(n−k)⋅fk(3)
(
3
)
+
s
n
(3)+s_n
(3)+sn 得
n
s
n
−
T
n
+
s
n
=
∑
k
=
1
n
(
n
−
k
)
⋅
f
k
+
s
n
=
∑
k
=
1
n
(
n
−
k
)
⋅
f
k
+
∑
k
=
1
n
f
k
=
∑
k
=
1
n
(
n
+
1
−
k
)
⋅
f
k
=
∑
k
=
1
n
+
1
(
n
+
1
−
k
)
⋅
f
k
=
(
n
+
1
)
s
n
+
1
−
T
n
+
1
\begin{aligned} ns_n-T_n+s_n&=\sum^{n}_{k=1}{(n-k)·f_k}+s_n\\ &=\sum^{n}_{k=1}{(n-k)·f_k}+\sum^n_{k=1}{f_k}\\ &=\sum^{n}_{k=1}{(n+1-k)·f_k}\\ &=\sum^{n+1}_{k=1}{(n+1-k)·f_k}\\ &=(n+1)s_{n+1}-T_{n+1} \end{aligned}
nsn−Tn+sn=k=1∑n(n−k)⋅fk+sn=k=1∑n(n−k)⋅fk+k=1∑nfk=k=1∑n(n+1−k)⋅fk=k=1∑n+1(n+1−k)⋅fk=(n+1)sn+1−Tn+1
设
p
k
=
k
s
k
−
T
k
p_k=ks_k-T_k
pk=ksk−Tk,于是我们得到
p
p
p 的递推关系式
p
k
=
p
k
−
1
+
s
k
−
1
p_k=p_{k-1}+s_{k-1}
pk=pk−1+sk−1所以我们可以构造矩阵
A
i
′
=
[
p
i
s
i
f
i
f
i
−
1
]
A_i'=\left[ \begin{array}{ccc} p_i&s_i&f_i&f_{i-1} \end{array} \right]
Ai′=[pisififi−1]
B
′
=
[
1
1
0
0
0
1
1
0
0
0
1
1
0
0
1
0
]
B'=\left[ \begin{array}{ccc} 1&1&0&0\\ 0&1&1&0\\ 0&0&1&1\\ 0&0&1&0 \end{array} \right]
B′=⎣⎢⎢⎡1000110001110010⎦⎥⎥⎤
使
A
i
′
×
B
′
=
A
i
+
1
′
A'_i\times B'=A'_{i+1}
Ai′×B′=Ai+1′
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
int n,m;
struct node{
ll a[5][5];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
void pt(){
printf("%lld",(a[1][2]*n%m-a[1][1]+m)%m);
}
}s,t,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
int main(){
scanf("%d%d",&n,&m);
s.x=1;s.y=4;s.a[1][2]=s.a[1][3]=s.a[1][4]=1;
t.x=4;t.y=4;t.a[1][1]=t.a[2][1]=t.a[2][2]=t.a[3][2]=t.a[3][3]=t.a[4][3]=t.a[3][4]=1;
ans=T(s,QT(t,n-1));
ans.pt();
}
题目描述 loj10225 \text{loj10225} loj10225
原题来自:SCOI 2009
Windy 在有向图中迷路了。 该有向图有 n n n 个节点,Windy 从节点 0 0 0 出发,他必须恰好在 T T T 时刻到达节点 。
现在给出该有向图,你能告诉 Windy 总共有多少种不同的路径吗?
注意:Windy 不能在某个节点逗留,且通过某有向边的时间严格为给定的时间。
输入格式
第一行包含两个整数
n
n
n,
T
T
T;
接下来有
n
n
n 行,每行一个长度为
n
n
n 的字符串。第
i
i
i 行第
j
j
j 列为
0
0
0 表示从节点
i
i
i 到节点
j
j
j 没有边,为
1
1
1 到
9
9
9 表示从节点
i
i
i 到节点
j
j
j 需要耗费的时间。
输出格式
包含一个整数,可能的路径数,这个数可能很大,只需输出这个数除以 2009 2009 2009 的余数。
样例输入 1
2 2
11
00
样例输出 1
1
样例输入 2
5 30
12045
07105
47805
12024
12345
样例输出 2
852
数据范围与提示
对于 100 % 100\% 100% 的数据,满足 2 ≤ n ≤ 10 , 1 ≤ T ≤ 1 0 9 2\leq n\leq 10,1\leq T\leq 10^9 2≤n≤10,1≤T≤109。
Solution 10225 \text{Solution 10225} Solution 10225
n
n
n 的规模和边权都很小,考虑拆点,则时间复杂度为
O
(
n
T
)
O(n^T)
O(nT)。
如何把
T
T
T 优化成
log
T
\log T
logT 呢?观察输入格式,我们很自然地想到矩阵,尝试使用矩阵快速幂优化。
设从
i
i
i 到
j
j
j 的边权为
u
i
j
u_{ij}
uij。考虑当边权为
0
0
0 或
1
1
1 时,从
i
i
i 到
j
j
j 路径长为
T
T
T 的路径数
s
i
j
T
=
∑
k
=
1
n
(
∑
u
=
T
)
s_{ijT}=\sum^{n}_{k=1}{(\sum u=T)}
sijT=k=1∑n(∑u=T)
如果构造矩阵
U
=
{
u
i
j
∣
i
,
j
∈
[
1
,
n
]
}
U=\{u_{ij}|i,j\in [1,n]\}
U={uij∣i,j∈[1,n]}
S
=
U
T
S=U^T
S=UT
则
S
i
j
=
s
i
j
T
(
∀
i
,
j
)
S_{ij}=s_{ijT}\ (\forall i,j)
Sij=sijT (∀i,j)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
int n,t;
const int m=2009;
char str[20][20];
struct node{
ll a[110][110];
int x,y;
node(){
x=y=0;
memset(a,0,sizeof(a));
}
}s,ans;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
node c;c.x=n;c.y=n;
for(reg int i=1;i<=n;++i)
c.a[i][i]=1;
while(b){
if(b&1) c=T(c,a);
a=T(a,a);b>>=1;
}
return c;
}
int main(){
scanf("%d%d",&n,&t);
for(reg int i=1;i<=n;++i){
scanf("%s",str[i]+1);
for(reg int j=1;j<=n;++j)
str[i][j]-='0';
}
for(reg int i=1;i<=n;++i){
for(reg int j=1;j<=8;++j)
s.a[(i-1)*9+j][(i-1)*9+j+1]=1;
for(reg int j=1;j<=n;++j)
if(str[i][j])
s.a[(i-1)*9+str[i][j]][(j-1)*9+1]=1;
}
n*=9;s.x=s.y=n;
ans=QT(s,t);
printf("%d",ans.a[1][n-8]);
}
题目描述 情书
求
∑
k
=
1
n
f
k
+
∑
k
=
1
n
c
k
(
m
o
d
  
m
)
\sum_{k=1}^{n}f_k+\sum_{k=1}^{n}{c^k}\quad(\mod m)
k=1∑nfk+k=1∑nck(modm)
的值,其中
∀
i
>
2
\forall i>2
∀i>2 有
f
i
=
a
⋅
f
i
−
1
+
b
⋅
f
i
−
2
f_i=a·f_{i-1}+b·f_{i-2}
fi=a⋅fi−1+b⋅fi−2
输入描述
一行 7 7 7 个整数,分别为 a , b , c , n , m , f 1 , f 2 a,b,c,n,m,f_1,f_2 a,b,c,n,m,f1,f2,相邻两数以一个空格隔开。
输出描述
一行,一个整数,表示答案。
输入样例
1 1 1 3 5 1 1 \ \ \ \ \text{1 1 1 3 5 1 1} 1 1 1 3 5 1 1
输出样例
2 \ \ \ \ 2 2
数据规模与约定
对于 100 % 100\% 100% 的数据,有 0 ≤ a , b , c , n , m , f i ≤ 2 31 0\leq a,b,c,n,m,f_i\leq2^{31} 0≤a,b,c,n,m,fi≤231,其中 i ∈ [ 1 , n ] i\in[1,n] i∈[1,n]。
Solution 5 \text{Solution 5} Solution 5
Pt.1 求 s k s_k sk 的值
仿照例2知, a , b a,b a,b 是矩阵 B B B 的两个元素,请读者自己尝试推导。
Pt.2 求 ∑ k = 1 n c k \sum_{k=1}^{n}{c^k} ∑k=1nck
简单等比数列求和。
设
S
=
∑
k
=
1
n
c
k
...(4)
S=\sum_{k=1}^{n}{c^k}\quad\text{...(4)}
S=k=1∑nck...(4)
则
c
S
=
∑
k
=
1
n
c
k
+
1
...(5)
cS=\sum_{k=1}^{n}{c^{k+1}}\quad\text{...(5)}
cS=k=1∑nck+1...(5)
(
5
)
−
(
4
)
(5)-(4)
(5)−(4) 得
(
c
−
1
)
S
=
c
k
+
1
−
c
(c-1)S=c^{k+1}-c
(c−1)S=ck+1−c
S
=
c
k
+
1
−
c
c
−
1
S=\frac{c^{k+1}-c}{c-1}
S=c−1ck+1−c
使用乘法逆元和快速幂即可完成求解。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define reg register
typedef long long ll;
struct node{
int x,y;
ll a[5][5];
node(){
memset(a,0,sizeof(a));
x=y=0;
}
}s,t;
ll a,b,c;
ll n,m,x,y,son;
ll sum=0;
node T(node a,node b){
node c;c.x=a.x;c.y=b.y;
for(reg int i=1;i<=c.x;++i)
for(reg int j=1;j<=c.y;++j)
for(reg int k=1;k<=a.y;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%m;
return c;
}
node QT(node a,int b){
if(b<=1) return a;
node c=QT(a,b/2);
if(b%2)
return T(T(c,c),a);
else
return T(c,c);
}
void exgcd(ll A,ll B,ll&x,ll&y){
if(!B){
x=1;y=0;
return;
}
exgcd(B,A%B,x,y);
ll t=x;x=y;y=t-A/B*y;
}
ll qt(ll a,int b){
if(b==1) return a;
ll e=qt(a,b/2)%m;
if(b%2)
return (e*e)%m*a%m;
else
return (e*e)%m;
}
int main(){
scanf("%lld%lld%lld%lld%lld%lld%lld",&a,&b,&c,&n,&m,&s.a[1][1],&s.a[1][2]);c%=m;
s.a[1][3]=s.a[1][1]+s.a[1][2];s.a[1][4]=s.a[1][1];
s.x=1;s.y=4;t.x=t.y=4;
t.a[2][1]=t.a[3][3]=t.a[3][4]=t.a[4][4]=1;
t.a[1][2]=t.a[1][3]=b;t.a[2][2]=t.a[2][3]=a;
s=T(s,QT(t,n-2));
exgcd(c-1,m,x,y);son=(qt(c,n+1)-c+m)%m;x=(x%m+m)%m;
printf("%lld",(((s.a[1][3]*n)%m-s.a[1][4]+m)%m+(x*son)%m+m)%m);
}