d644: 壞脾氣小小皮
Tags: fast-exponentiation, fibonacci, matrix
出處:https://zerojudge.tw/ShowProblem?problemid=d644
提交:https://zerojudge.tw/Submissions?problemid=d644&account=allllllan123456
問題:定義數列 $a_1=1$、$a_2=1$,對於 $k\ge3$,$a_k = \begin{cases}a_{k-1}+a_{k-2} &\mbox{if }k \not\equiv 1\\a_{k-1}+a_{k-2}-1 & \mbox{if }k \equiv 1\end{cases}\pmod 3$,現在給你一個 int 的正整數 $n$,請求出 $a_n\pmod{100019}$。
解法:看到要找類費式數列的一般項數高達 int 上界的值,就要直接聯想到矩陣乘法搭配快速冪。那為了程式上實作的方便,讓 index 從 0 起算,定義 $b_k=a_{k+1}\ (\text{for}\ k\ge0)$,那麼矩陣乘法的一般式 $(t\ge0)$ 為:$\begin{bmatrix}b_{3(t+1)+0}\\b_{3(t+1)+1}\\b_{3(t+1)+2}\\-1\end{bmatrix}=\begin{bmatrix}0 & 1 & 1 & 1\\0 & 1 & 2 & 1\\0 & 2 & 3 & 2\\0 & 0 & 0 & 1\end{bmatrix}\begin{bmatrix}b_{3t+0}\\b_{3t+1}\\b_{3t+2}\\-1\end{bmatrix}$,數列三項三項一組再加上一個位移常量總共四項,矩陣的第一個列 $[0\ \ 1\ \ 1\ \ 1]$ 正處理了 $k\equiv0\pmod 3$ 的項,而第二列根據定義理論上要是它上方兩個列的總和,但是它上面只有一列,因為再更上面一列其實就對應到右側行向量的第三項,於是可以取而代之地直接對同一列的第三項加一,至於第三列才如剛剛所說的可以直接是第一列和第二列的相加,最後第四列必須維持位移常量才行。根據此遞迴式,易知一般項 $b_k$ 的求法便是 $\begin{bmatrix}0 & 1 & 1 & 1\\0 & 1 & 2 & 1\\0 & 2 & 3 & 2\\0 & 0 & 0 & 1\end{bmatrix}^{\displaystyle\lfloor k/3\rfloor}\begin{bmatrix}1\\1\\2\\-1\end{bmatrix}$ 之結果行向量的第 $k\pmod3$ 個元素。
1#include <bits/stdc++.h>
2using namespace std;
3
4struct matrix {
5 int e[4][4] = {{0,1,1,1},{0,1,2,1},{0,2,3,2},{0,0,0,1}};
6 matrix operator*(const matrix& m2) const {
7 matrix ans;
8 for (int i=0; i<4; i++)
9 for (int j=0; j<4; j++) {
10 long long temp = 0;
11 for (int k=0; k<4; k++)
12 temp += (long long)e[i][k] * m2.e[k][j];
13 ans.e[i][j] = temp % 100019;
14 }
15 return ans;
16 }
17};
18
19int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); // IO 優化
20 matrix mb; int ini2[4], ini[]={1,1,2,-1}, n; cin >> n; n--;
21 for (unsigned t=n/3; t; t>>=1) {
22 if (t & 1) {
23 memset(ini2, 0, sizeof ini2);
24 for (int i=0; i<4; i++) {
25 long long temp = 0;
26 for (int k=0; k<4; k++)
27 temp += (long long)mb.e[i][k] * ini[k];
28 ini2[i] = temp % 100019;
29 }
30 memcpy(ini, ini2, sizeof ini2);
31 }
32 mb = mb * mb;
33 }
34 cout << ini[n % 3] << '\n';
35 return 0;
36}