1. 程式人生 > >【學習筆記】快速冪+矩陣+矩陣乘法+矩陣快速冪

【學習筆記】快速冪+矩陣+矩陣乘法+矩陣快速冪

今天晚上我學習了矩陣

1、快速冪

通常,我們要算bpmodk是這麼算的:

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

顯然,時間複雜度是O(p)
但是萬一p很大,比如p109呢,暴力顯然是Tle了
那麼快速冪可以將O(p)優化成O(logp)級別
同樣是算bpmodk,快速冪的核心部分:

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;
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的矩陣

那麼矩陣的運算呢?

  • 矩陣加法
    矩陣的加法就是對應位置加起來,即cij=aij+bij
  • 矩陣減法
    矩陣的減法與加法類似,即cij=aijbij
  • 矩陣乘一個數
    簡單的,就是矩陣中每一個數乘當前數
  • 矩陣乘法
    矩陣的乘法比較複雜,而且必須是m*p的矩陣與p*n的矩陣相乘得到一個m*n的矩陣
    公式是
    cij=sigma(aikbkj)

3、矩陣快速冪

顧名思義,就是矩陣的快速冪。。
放一道模板題有助消化
針對這道題來講
暴力矩陣乘法會Tle
探究一下矩陣的運算滿足的法則

  • 乘法交換律,顯然不行,因為矩陣的乘法是有要求的
  • 乘法結合律,滿足,即(AB)C=A(BC)

滿足結合律,說明什麼?
可以用快速冪!!!
那麼,矩陣的快速冪我們給他一個名字:矩陣快速冪

如何實現?
套快速冪的模板!只要把普通乘法部分改成矩陣乘法就好了

不過有一個問題:矩陣的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
首先,我們需要推匯出一個矩陣,使得通過乘這個矩陣,我們手中的數列可以發展下去
由斐波那契公式fi=fi1+fi2可知第i項只與前兩項有關
那麼可以構造一個列向量B:
這裡寫圖片描述
因為:
fi=fi11+fi21
fi1=fi11+fi20
所以我們可以建立矩陣:
這裡寫圖片描述
那就先求後面那個矩陣的次冪,再乘列向量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
題目給出:
a1=a2=a3=1
ai=ai1+ai3(i>3)
我們按照剛才斐波那契的套路構造矩陣
首先,構造列向量
這裡寫圖片描述
因為:
fi=fi11+fi20+fi31