ZROI#1119
ZROI#1119
看起来非常怪异...因为之前知道 \(Fibonacii\) 数列有通项公式,所以就一直以为这题是 \(F\) 的递推转通项...
万万没想到,这竟然是个矩阵加速递推...
\(⑧\) 说了,伤心,直接上式子:
\[\begin{aligned} F_{n} &=\sum_{i=0}^{n} f_{i} f_{n-i} \\ &=\sum_{i=0}^{n}\left(f_{i-1}+f_{i-2}\right) f_{n-i}+f_{1} f_{n-1}+f_{0} f_{n} \\ &=\sum_{i=0}^{n-1} f_{i} f_{n-i-1}-f_{0} f_{n-1}+\sum_{i=0}^{n-2} f_{i} f_{n-i-2}+f_{1} f_{n-1}+f_{0} f_{n} \\ &=F_{n-1}+F_{n-2}+f_{1} f_{n-1}-f_{0} f_{n-1}+f_{0} f_{n} \\ &=F_{n-1}+F_{n-2}+f_{n} \end{aligned}
\]
所以我们有了递推公式,我们发现这个递推公式是和 \(F\) 以及 \(f\) 的相邻两项相关的,所以我们的初始矩阵和目标矩阵应保留至少四维状态.
而因为答案要求的是 \(F\) 的前缀和,所以要多加一维用以辅助递推,于是我们的转移矩阵应该是 \(5\times 5\) 的.
我们发现这个初始矩阵和目标矩阵应该是这个亚子:
\[\left[\begin{array}{lllll}{?} & {?} & {?} & {?} & {?} \\ {?} & {?} & {?} & {?} & {?} \\ {?} & {?} & {?} & {?} & {?} \\ {?} & {?} & {?} & {?} & {?} \\ {?} & {?} & {?} & {?} & {?}\end{array}\right]\left[\begin{array}{c}{F_{i}} \\ {F_{i-1}} \\ {f_{i}} \\ {f_{i-1}} \\ {a n s_{i}}\end{array}\right]=\left[\begin{array}{c}{F_{i+1}} \\ {F_{i}} \\ {f_{i+1}} \\ {f_{i}} \\ {a n s_{i+1}}\end{array}\right]
\]
问号矩阵是我们要推导的转移矩阵.
经过推导,可以得到转移矩阵是这样的:
\[\left[\begin{array}{lllll}{1} & {1} & {0} & {0} & {1} \\ {1} & {0} & {0} & {0} & {1} \\ {1} & {0} & {1} & {1} & {1} \\ {1} & {0} & {1} & {0} & {1} \\ {0} & {0} & {0} & {0} & {1}\end{array}\right]\left[\begin{array}{c}{F_{i}} \\ {F_{i-1}} \\ {f_{i}} \\ {f_{i-1}} \\ {a n s_{i}}\end{array}\right]=\left[\begin{array}{c}{F_{i+1}} \\ {F_{i}} \\ {f_{i+1}} \\ {f_{i}} \\ {a n s_{i+1}}\end{array}\right]
\]
至于如何推导,我还会有一篇博文专门讲解如何推导一个矩阵,在此不再赘述.
直接快乐地上一个矩阵快速幂即可.
初始矩阵是 \(1\times 5\) 的 \(:\{2,1,1,1,3\}.\)
需要注意的是,因为初始矩阵是从第一项开始的,每乘一次转移矩阵向后递推一次,所以我们需要的转移次数是 \(n-1\) 的.
愉快\(AC!\).
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <ctime>
#include <map>
#include <set>
#define MEM(x,y) memset ( x , y , sizeof ( x ) )
#define rep(i,a,b) for (int i = (a) ; i <= (b) ; ++ i)
#define per(i,a,b) for (int i = (a) ; i >= (b) ; -- i)
#define pii pair < int , int >
#define one first
#define two second
#define rint read<int>
#define rll read<LL>
#define LL long long
#define pb push_back
#define db double
#define mid ( ( l + r ) >> 1 )
using std::queue ;
using std::set ;
using std::pair ;
using std::max ;
using std::min ;
using std::priority_queue ;
using std::vector ;
using std::swap ;
using std::sort ;
using std::unique ;
using std::greater ;
template < class T >
inline T read () {
T x = 0 , f = 1 ; char ch = getchar () ;
while ( ch < '0' || ch > '9' ) {
if ( ch == '-' ) f = - 1 ;
ch = getchar () ;
}
while ( ch >= '0' && ch <= '9' ) {
x = ( x << 3 ) + ( x << 1 ) + ( ch - 48 ) ;
ch = getchar () ;
}
return f * x ;
}
const int mod = 998244353 ;
struct Matrix {
LL e[5][5] , line , row ;
inline void clear () { line = row = 0 ; MEM ( e , 0 ) ; return ; }
friend Matrix operator * (Matrix a , Matrix b) {
Matrix c ; c.clear () ; c.line = a.line ; c.row = b.row ;
rep ( k , 0 , a.row - 1 ) rep ( i , 0 , a.line - 1 ) rep ( j , 0 , b.row - 1 )
c.e[i][j] = ( c.e[i][j] + a.e[i][k] * b.e[k][j] % mod ) % mod ;
return c ;
}
} ;
inline Matrix quick (Matrix a , LL p) {
Matrix c ; c.clear () ; c.line = a.line ; c.row = a.row ;
rep ( i , 0 , c.line - 1 ) c.e[i][i] = 1 ;
while ( p ) {
if ( p & 1 ) c = c * a ;
a = a * a ; p >>= 1 ;
}
return c ;
}
LL n , k ; Matrix p ;
int main(){
n = rll () ; p.clear () ;
p.e[0][0] = p.e[1][0] = p.e[2][0] = 1 ;
p.e[3][0] = p.e[0][1] = p.e[2][2] = 1 ;
p.e[3][2] = p.e[0][4] = p.e[1][4] = 1 ;
p.e[2][4] = p.e[3][4] = p.e[4][4] = 1 ;
p.e[2][3] = 1 ; p.line = 5 ; p.row = 5 ;
Matrix ans = quick ( p , n - 1ll ) ; ;
Matrix pri ; pri.clear () ;
pri.line = 1 ; pri.row = 5 ;
pri.e[0][0] = 2 ; pri.e[0][1] = 1 ;
pri.e[0][2] = 1 ; pri.e[0][3] = 1 ;
pri.e[0][4] = 3 ; ans = pri * ans ;
printf ("%lld\n" , ans.e[0][4] ) ;
system ("pause") ; return 0 ;
}
May you return with a young heart after years of fighting.