標籤:
矩陣快速冪
首Crowdsourced Security Testing道矩陣
矩陣(Matrix)是一個按照長方陣列排列的複數或實數集合;
矩陣乘法:
定義:設A為 m×p 的矩陣,B為 p×n 的矩陣,那麼稱 m×n 的矩陣C為矩陣A與B的乘積,記作 C=A×B,其中矩陣C中的第 i 行第 j 列元素可以表示為:
知道矩陣乘法之後,比如菲波那切數列就是一個遞推式, F(n)=F(n-1)+F(n-2); 因為矩陣乘法,所以
設 矩陣 A為 矩陣 B 為 則 A*B 則為 F(n)= F(n-1)*1+F(n-2)*1因為我們需要的矩陣為 A*B 的矩陣為 所以反向推算到 B 矩陣 ,則 B矩陣是 ,所以 F(n) 的矩陣為 初始矩陣 * (B^(n-2)) n>=3;所得到的矩陣 A[0][0] 就是F(n) 的值,時間複雜度最佳化的地方就是在冪指數的那部分快速冪,節約了時間。所以關鍵就在於 從 遞推式構造矩陣 比如:
這個構造出來的矩陣就是
自己可以寫出這個矩陣,就明白了;下面給一個簡單的題目,練練手。
給一個簡單的練習題: 點擊開啟連結
#include <cstdio>#include <cstring>#include <cctype>#include <cmath>#include <set>#include <map>#include <list>#include <queue>#include <deque>#include <stack>#include <string>#include <bitset>#include <vector>#include <iostream>#include <algorithm>#include <stdlib.h>using namespace std;typedef long long LL;const int INF=2e9+1e8;const int MOD=1000007;const int MAX_SIZE=1005;const int MM=3;LL f1,f2,a,b,c,n;struct Mat{ LL maze[MM][MM]; void set_empty() { memset(maze,0,sizeof(maze)); }};Mat unit_Mat={ 1,0,0, 0,1,0, 0,0,1}; // 定義一個單位矩陣,任何一個矩陣 乘以 單位矩陣,其值等於本身;Mat operator *(Mat a,Mat b) // 重載運算子 * // 定義兩個矩陣的乘法,根據矩陣乘法的定義來寫{ Mat c; c.set_empty(); // 一定要設定為空白。清零數組;不然後面加等一個數會有垃圾值 LL i,j,k; for(i=0; i<MM; i++) { for(j=0; j<MM; j++) { for(k=0; k<MM; k++) { c.maze[i][j]+=a.maze[i][k] * b.maze[k][j]; c.maze[i][j] %= MOD; } } } return c;}Mat operator^(Mat a,LL N) // 最佳化時間的就在此處,// 類似與一般的int 數位快速冪求值,思想是一樣的。不懂可百度快速冪。{ Mat c=unit_Mat; while(N) { if(N&1) c=a*c; a=a*a; N>>=1; } return c;}void solve(){ Mat A,B; B.set_empty(); A.set_empty(); B.maze[0][0]=b,B.maze[1][0]=a,B.maze[2][0]=c; B.maze[0][1]=B.maze[2][2]=1; B=B^(n-2); A.maze[0][0]=f2,A.maze[0][1]=f1,A.maze[0][2]=1; LL ans=0; Mat C=A*B; printf("%lld\n",(C.maze[0][0]+MOD)%MOD);}int main(){ int times; scanf("%d",×); while(times--) { scanf("%lld %lld %lld %lld %lld %lld",&f1,&f2,&a,&b,&c,&n); if(n == 1) printf("%lld\n",(f1+MOD)%MOD); else if(n == 2) printf("%lld\n",(f2+MOD)%MOD); else solve(); // 其上位特判 因為公式的定義域是 n>=3; } return 0;}
矩陣快速冪 ——(遞推運算式)