【学习笔记】快速幂+矩阵+矩阵乘法+矩阵快速幂

版权声明:Fashion Education https://blog.csdn.net/ModestCoder_/article/details/82055465

今天晚上我学习了矩阵

1、快速幂

通常,我们要算 b p m o d k 是这么算的:

ans := 1;
for i := 1 to p do
    ans := ans * b mod k;

显然,时间复杂度是O(p)
但是万一p很大,比如 p 10 9 呢,暴力显然是Tle了
那么快速幂可以将O(p)优化成O(logp)级别
同样是算 b p m o d k ,快速幂的核心部分:

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的矩阵

那么矩阵的运算呢?

  • 矩阵加法
    矩阵的加法就是对应位置加起来,即 c i j = a i j + b i j
  • 矩阵减法
    矩阵的减法与加法类似,即 c i j = a i j b i j
  • 矩阵乘一个数
    简单的,就是矩阵中每一个数乘当前数
  • 矩阵乘法
    矩阵的乘法比较复杂,而且必须是m*p的矩阵与p*n的矩阵相乘得到一个m*n的矩阵
    公式是 c i j = s i g m a ( a i k b k j )

3、矩阵快速幂

顾名思义,就是矩阵的快速幂。。
放一道模板题有助消化
针对这道题来讲
暴力矩阵乘法会Tle
探究一下矩阵的运算满足的法则

  • 乘法交换律,显然不行,因为矩阵的乘法是有要求的
  • 乘法结合律,满足,即 ( A B ) C = A ( B C )

满足结合律,说明什么?
可以用快速幂!!!
那么,矩阵的快速幂我们给他一个名字:矩阵快速幂

如何实现?
套快速幂的模板!只要把普通乘法部分改成矩阵乘法就好了

不过有一个问题:矩阵的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
首先,我们需要推导出一个矩阵,使得通过乘这个矩阵,我们手中的数列可以发展下去
由斐波那契公式 f i = f i 1 + f i 2 可知第i项只与前两项有关
那么可以构造一个列向量B:
这里写图片描述
因为:
f i = f i 1 1 + f i 2 1
f i 1 = f i 1 1 + f i 2 0
所以我们可以建立矩阵:
这里写图片描述
那就先求后面那个矩阵的次幂,再乘列向量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
题目给出:
a 1 = a 2 = a 3 = 1
a i = a i 1 + a i 3 ( i > 3 )
我们按照刚才斐波那契的套路构造矩阵
首先,构造列向量
这里写图片描述
因为:
f i = f i 1 1 + f i 2 0 + f i 3 1
f i 1 = f i 1 1 + f i 2 0 + f i 3 0
f i 2 = f i 1 0 + f i 2 1 + f i 3 0
所以初始矩阵就构造出来了:
这里写图片描述
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.

猜你喜欢

转载自blog.csdn.net/ModestCoder_/article/details/82055465