一直有乙個想法,感覺自己很多基礎演算法不是很紮實,想要找個機會寫一些演算法的整理,順便自己總結一些實用的模板。
最近偶然在訓練賽中連續做了2道思維+矩陣快速冪的題目,碰巧有時間,就以矩陣快速冪作為這個系列部落格的開始吧。
如果想要了解矩陣快速冪,首先要了解什麼叫做快速冪。
舉個例子,如果讓你求2^10的值,乙個for迴圈可以輕鬆地解決問題,但是如果是2^10000000000000呢?
且不管這個值能否表示出來,單單說for迴圈的時間複雜度o(n)就注定不能直接暴力求解。
當然,為了求得這個解,我們一般要求答案對於某個數取模,常用的mod值有10007,1000000007。
由此,我們可以看出,當問題存在超時+取模的限制時,我們需要一種新的演算法,即快速冪。
快速冪是基於二分思想的一種時間複雜度為o(lgn)的演算法。
我們可以考慮乙個例子,如果要求2^10的值,我們能否這樣算:
首先把2^10分解成(2^5)*(2^5),其次把2^5
分解成2*(2^4),然後將2^4分成(2^2)*(2^2),最後把2^2變成2*2。
這樣,我們就將2^10變成了:(2*(2*2)^2)^2。這樣我們只需要計算4次乘法就可以得到2^10的值,而線性的演算法需要10次,快速冪進行了極大地優化。
一般地,對於a^b來說,當b為偶數時,我們可以寫成(a^(b/2))^2;當b為奇數時,可以寫成a*(a^(b-1))。
所以,經過快速冪演算法優化後的quick_pow計算只需要log(b)次!b越大,這個優化就越明顯!
模板**如下:
#include #include快速冪模板#define mod 1000000007typedef
long
long
intll
using
namespace
std;
ll quick_pow(
int a,int
b)
return
res;
}
了解了快速冪的基本思想與**實現,我們就要來看看矩陣快速冪。其實矩陣快速冪的基本思想和普通快速冪是一樣的。
對於矩陣乘法,模板如下:
#include #include矩陣乘法#include
#include
#define mod 1000000007typedef
long
long
intll;
using
namespace
std;
struct
matrix
matrix
operator* (const matrix &p)
res.a[i][j]%=mod;}}
return
res;
}}init,unit;
首先來看乙個例子:如果問題是求解斐波那契數列的第1000000000項對於mod取模的值,我們應該如何去做?
從快速冪我們知道對於這個問題線性的計算是肯定超時的,所以我們依舊採用快速冪的思想。
對於斐波那契數列我們有如下規律(以下為矩陣):
f(n+1) f(n) 乘以 1 1 結果是 f(n+1)+f(n) f(n+1) 即:f(n+2) f(n+1)
f(n) f(n-1) 1 0 f(n)+f(n-1) f(n) f(n+1) f(n)
這是我們會驚奇的發現 當我們用斐波那契數列組成的矩陣乘以乙個特定矩陣時會得到下乙個斐波那契數的值。
所以不難想象,我們只要知道數列的前3項,用他們組成的矩陣乘以這個特定矩陣的k次冪就能得到任意項的斐波那契數,並且時間複雜度是o(lgn)的!
所以,到這裡我們就要知道如何去找這個特定矩陣。
一般地,如果有通項公式:f(n)=a*f(n-1)+b*f(n-2)+c*f(n-3)……(這裡我們以3個為例),若f(1)==p,f(2)==q,f(3)==r
我們設定乙個init矩陣表示初始值:
r 0 0
q 0 0
p 0 0
乙個unit矩陣表示那個特定矩陣:
a b c
1 0 0
0 1 0
這樣,unit矩陣左乘init矩陣等於:
ap+bq+cs 0 0
p 0 0
q 0 0
這樣我們就構造出了乙個矩陣,表示出了整個數列的遞推關係:
a b c f(3) 0 0 f(n+2) 0 0
(1 0 0) 的n次冪 乘以 f(2) 0 0 等於 f(n+1) 0 0
0 1 0 f(1) 0 0 f(n) 0 0
當然,構造這樣的矩陣的方法不一,此方法只是較為通用的方法,對於某些通項公式可以找到更簡便的矩陣使得矩陣快速冪成立。
矩陣快速冪模板如下:
#include #include矩陣快速冪#include
#include
#define mod 1000000007typedef
long
long
intll;
using
namespace
std;
struct
matrix
matrix
operator* (const matrix &p)
res.a[i][j]%=mod;}}
return
res;
}}init,unit;
matrix quick_pow(matrix unit,matrix init,
intk)
return
init;
}void
init_matrix()
主函式中首先對init和unit矩陣進行初始化,然後再呼叫quick_pow()。
小tips:關於如何識別矩陣快速冪的問題。
一般來說,題目中如果有」答案對於mod取模「這句話時,並且操作次數巨大,我們就可以考慮使用快速冪或矩陣快速冪。
關於題目中有」取模「的說法時,一般來說有幾種可能。一是快速冪,二是dp,三是組合數學。
矩陣快速冪模板
剛學了矩陣快速冪,花了點時間把之前的 修改一下寫成了矩陣類,就當做模板了.話不多說下面貼 首先是標頭檔案和巨集定義什麼的 include include include using namespace std define inf 1000000000 define maxm 20 define m...
矩陣快速冪模板
矩陣快速冪 o log n nyoj301 580ms 時間限制 1000 ms 記憶體限制 65535 kb 難度 4 描述 給你乙個遞推公式 f x a f x 2 b f x 1 c 並給你f 1 f 2 的值,請求出f n 的值,由於f n 的值可能過大,求出f n 對1000007取模後的...
矩陣快速冪模板
struct mat mat operator const mat c return res 上面是我的基本矩陣快速冪模板,其實矩陣快速冪難的不是你怎麼寫,難的是你矩陣怎麼構造。矩陣的構造,就是找遞推關係。要把需要用到的遞推關係包含操作矩陣上去。找到合適的初始向量和合適的操作矩陣,你基本就可以完成題...