今天晚上我学习了矩阵
1、快速幂
通常,我们要算 是这么算的:
ans := 1;
for i := 1 to p do
ans := ans * b mod k;
显然,时间复杂度是O(p)
但是万一p很大,比如
呢,暴力显然是Tle了
那么快速幂可以将O(p)优化成O(logp)级别
同样是算
,快速幂的核心部分:
function pow(b,p,k:int64):int64;
begin
if p = 0 then exit(1 mod k);
pow := pow(b,p >> 1,k);
pow := pow * pow mod k;
if p mod 2 = 1 then pow := pow * b mod k;
end;
LuoGu快速幂模板题
Code:
var
b,p,k:int64;
function pow(b,p,k:int64):int64;
begin
if p = 0 then exit(1 mod k);
pow := pow(b,p >> 1,k);
pow := pow * pow mod k;
if p mod 2 = 1 then pow := pow * b mod k;
end;
begin
readln(b,p,k);
writeln(b,'^',p,' mod ',k,'=',pow(b,p,k));
end.
2、矩阵
关于矩阵,这里讲的很好
矩阵,就是像这样的:
8 2 3 6
1 8 3 6
2 8 3 7
显然,这是一个3*4的矩阵
那么矩阵的运算呢?
- 矩阵加法
矩阵的加法就是对应位置加起来,即 - 矩阵减法
矩阵的减法与加法类似,即 - 矩阵乘一个数
简单的,就是矩阵中每一个数乘当前数 - 矩阵乘法
矩阵的乘法比较复杂,而且必须是m*p的矩阵与p*n的矩阵相乘得到一个m*n的矩阵
公式是
3、矩阵快速幂
顾名思义,就是矩阵的快速幂。。
放一道模板题有助消化
针对这道题来讲
暴力矩阵乘法会Tle
探究一下矩阵的运算满足的法则
- 乘法交换律,显然不行,因为矩阵的乘法是有要求的
- 乘法结合律,满足,即
满足结合律,说明什么?
可以用快速幂!!!
那么,矩阵的快速幂我们给他一个名字:矩阵快速幂
如何实现?
套快速幂的模板!只要把普通乘法部分改成矩阵乘法就好了
不过有一个问题:矩阵的0次幂是什么?
因为任何数的0次幂是1,我们也发现了一个特殊的矩阵:左上右下对角线为1,其他都为0的矩阵
任何数乘1都为本身,任何矩阵乘这个矩阵也不变,那么我们初始化成这个矩阵就好了
Code:
const mo = 1000000007;
type
ar = array[0..100,0..100] of int64;
var
a,b,c:ar;
n,m:int64;
i,j:longint;
procedure mul(a,b:ar);
var
i,j,k:longint;
begin
fillchar(c,sizeof(c),0);
for i := 1 to n do
for j := 1 to n do
for k := 1 to n do
c[i][j] := (c[i][j] + a[i][k] * b[k][j]) mod mo;
end;
procedure update(var a:ar;b:ar);
var
i,j:longint;
begin
for i := 1 to n do
for j := 1 to n do
a[i][j] := b[i][j];
end;
procedure pow(p:int64);
var
i,j,k:longint;
begin
if p = 0 then
begin
for i := 1 to n do a[i][i] := 1;
exit;
end;
pow(p >> 1);
mul(a,a);
update(a,c);
if p mod 2 = 1 then
begin
mul(a,b);
update(a,c);
end;
end;
begin
readln(n,m);
for i := 1 to n do
begin
for j := 1 to n do
read(b[i][j]);
readln;
end;
pow(m);
for i := 1 to n do
begin
for j := 1 to n do write(a[i][j],' ');
writeln;
end;
end.
用Fibonacci数列练练手:LuoGu1962
首先,我们需要推导出一个矩阵,使得通过乘这个矩阵,我们手中的数列可以发展下去
由斐波那契公式
可知第i项只与前两项有关
那么可以构造一个列向量B:
因为:
所以我们可以建立矩阵:
那就先求后面那个矩阵的次幂,再乘列向量B得到答案
Code:
const mo = 1000000007;
type
ar = array[0..10,0..10] of int64;
var
a,b,c:ar;
d,e:array[0..100] of int64;
i,j:longint;
n:int64;
procedure mul(a,b:ar);
var
i,j,k:longint;
begin
fillchar(c,sizeof(c),0);
for i := 1 to 2 do
for j := 1 to 2 do
for k := 1 to 2 do
c[i][j] := (c[i][j] + a[i][k] * b[k][j]) mod mo;
end;
procedure update(var a:ar;b:ar);
var
i,j:longint;
begin
for i := 1 to 2 do
for j := 1 to 2 do
a[i][j] := b[i][j];
end;
procedure pow(p:int64);
var
i:longint;
begin
if p = 0 then
begin
for i := 1 to 2 do a[i][i] := 1;
exit;
end;
pow(p >> 1);
mul(a,a);
update(a,c);
if p mod 2 = 1 then
begin
mul(a,b);
update(a,c);
end;
end;
begin
readln(n);
if n <= 2 then
begin
writeln(1);
halt;
end;
dec(n,2);
b[1][1] := 1;
b[1][2] := 1;
b[2][1] := 1;
pow(n);
fillchar(c,sizeof(c),0);
d[1] := 1; d[2] := 1;
for i := 1 to 2 do
for j := 1 to 2 do
e[i] := (e[i] + a[i][j] * d[j]) mod mo;
writeln(e[1]);
end.
再来一个模板:LuoGu1939
题目给出:
我们按照刚才斐波那契的套路构造矩阵
首先,构造列向量
因为:
所以初始矩阵就构造出来了:
Code:
const mo = 1000000007;
type
ar = array[0..10,0..10] of int64;
var
a,b,c:ar;
d,e:array[0..100] of int64;
i,j,p:longint;
q,n:int64;
procedure mul(a,b:ar);
var
i,j,k:longint;
begin
fillchar(c,sizeof(c),0);
for i := 1 to 3 do
for j := 1 to 3 do
for k := 1 to 3 do
c[i][j] := (c[i][j] + a[i][k] * b[k][j]) mod mo;
end;
procedure update(var a:ar;b:ar);
var
i,j:longint;
begin
for i := 1 to 3 do
for j := 1 to 3 do
a[i][j] := b[i][j];
end;
procedure pow(p:int64);
var
i:longint;
begin
if p = 0 then
begin
for i := 1 to 3 do a[i][i] := 1;
exit;
end;
pow(p >> 1);
mul(a,a);
update(a,c);
if p mod 2 = 1 then
begin
mul(a,b);
update(a,c);
end;
end;
begin
readln(q);
b[1][1] := 1; b[1][3] := 1;
b[2][1] := 1; b[3][2] := 1;
d[1] := 1; d[2] := 1; d[3] := 1;
for p := 1 to q do
begin
readln(n);
if n <= 3 then
begin
writeln(1);
continue;
end;
dec(n,3);
fillchar(a,sizeof(a),0);
pow(n);
fillchar(e,sizeof(e),0);
for i := 1 to 3 do
for j := 1 to 3 do
e[i] := (e[i] + a[i][j] * d[j]) mod mo;
writeln(e[1]);
end;
end.