Fork me on GitHub

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!$.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#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 ;
}