洛谷 P1939 【模板】矩陣加速(數列):優化遞推式的方法——矩陣快速冪
阿新 • • 發佈:2018-12-24
在大多數情況下,O(n)的效率都是值得驕傲的,然而,有時候並不是,比如如何在一秒鐘內算出一個遞推式的第1e9項,很明顯O(n)不行了。
然而常數級又不太現實,除非你的數學非常好,這題又比較簡單,你推了一個特徵方程的通項公式……
所以考慮log的做法:矩陣快速冪
如果你還不知道矩陣快速冪是什麼,請走這邊:傳送門
對於這道題,嗯,模板嘛,已經告訴你了式子,就只需要考慮矩陣了,對於整個過程,我們只需要兩個矩陣,初始矩陣和轉置矩陣:
先看這個特徵方程F[i] = F[i - 1] + F[i - 3],那麼就有一個矩陣如下
我們的目標矩陣就是
那麼,針對這個矩陣我們如何轉置呢?
先看目標矩陣第一個:F[i]
F[i] = F[i - 1] + F[i - 3]
那麼,由矩陣乘法,轉置矩陣第一行,似乎就定了:1 0 1
同樣的,二三行就是1 0 0 和 0 1 0
整個矩陣如下:
程式碼:
#include <bits/stdc++.h> const int mod = 1000000007; int n; inline int read() { int ch;int x = 0;int f = 1;ch=getchar(); while (ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); ch=='-'?f=-1:x=ch-'0',ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } struct Mat { long long mat[3][3]; }; Mat operator * (Mat a, Mat b) { Mat c; memset(c.mat, 0, sizeof(c.mat)); for(int i = 0; i < 3; i++) { for(int k = 0; k < 3; k++) { for(int j = 0; j < 3; j++) { c.mat[i][j] += (a.mat[i][k] % mod) * (b.mat[k][j] % mod) % mod; c.mat[i][j] %= mod; } } } return c; } Mat operator ^ (Mat a, long long k) { Mat c; for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) c.mat[i][j] = (i == j); while(k) { if(k & 1) c = c * a; a = a * a; k >>= 1; } return c; } Mat init; Mat fi; int t; signed main() { scanf("%d", &t); while(t--) { memset(init.mat, 0, sizeof(init.mat)); memset(fi.mat, 0, sizeof(fi.mat)); scanf("%d", &n); init.mat[0][0] = 1; init.mat[0][2] = 1; init.mat[1][0] = 1; init.mat[2][1] = 1; fi.mat[0][0] = 1; fi.mat[1][0] = 1; fi.mat[2][0] = 1; if(n <= 3) printf("%d\n", 1); else{ init = init ^ (n - 3); fi = init * fi; printf("%d\n", fi.mat[0][0]); } } return 0; }