题解:Luogu-P1349 广义斐波那契数列

解题报告

直接递推时间复杂度是 \(O(n)\) 的,会超时。由于是递推求解,考虑矩阵加速递推。

原递推式 \(a_n=a_{n-1}+a_{n-2}\) 写成矩阵递推形式为:

\[\begin{bmatrix} a_n & a_{n-1} \end{bmatrix} = \begin{bmatrix} a_{n-1} & a_{n-2}\end{bmatrix} \times A = ... = \begin{bmatrix} a_{2} & a_{1}\end{bmatrix} \times A^{n-2} \]

其中 \(A\) 是 \(2 \times 2\) 的矩阵,也就是转移矩阵。求解出 \(A\) 就可以利用矩阵快速幂解决本题。

设 \(A\) 为 \(\begin{bmatrix} a & b \\ c & d \end{bmatrix}\),利用第一个等式,我们可以得出以下式子:

\[\begin{cases} a_3=p\times a_2+q\times a_1=a\times a_2+c\times a_1\\ a_2=b\times a_2 + d\times a_1\\ a_2=p\times a_1+q\times a_0=a\times a_1+c\times a_0\\ a_1=b\times a_1+d\times a_0 \end{cases}\]

这些式子分别用 \(n=3,n=2\) 代入矩阵获得。

将 \(a_0=0\) 代入式子,解得 \(a=p,b=1,c=q,d=0\)。则 \(A\) 为 \(\begin{bmatrix} p & 1 \\ q & 0 \end{bmatrix}\)。\(\begin{bmatrix} a_{2} & a_{1}\end{bmatrix} \times A^{n-2}\) 的第一行第一个数就是 \(a_n\)。当然,如果 \(n\le 2\) 要特判。

最后矩阵快速幂求解即可,时间复杂度 \(O(\log n)\)。

参考代码

cpp 复制代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
template <typename T>
inline void read(T &x){char c;x=0;int fu=1;c=getchar();while(c>57||c<48){if(c==45){fu=-1;}c=getchar();}while(c<=57&&c>=48){x=(x<<3)+(x<<1)+c-48;c=getchar();}x*=fu;}
template <typename T>
inline void write(T x){if(x<0){putchar(45);x=-x;}if(x>9){write(x/10);}putchar(x%10+48);}
int p, q, a1, a2, n, m; 
struct MATRIX {
	int mp[3][3]; 
}A, Ans; 
MATRIX operator * (const MATRIX& X, const MATRIX& Y) {
	MATRIX res; memset(res.mp, 0, sizeof(res.mp)); 
	for(int i = 1; i <= 2; i++) {
		for(int j = 1; j <= 2; j++) {
			for(int k = 1; k <= 2; k++) {
				res.mp[i][j] = (res.mp[i][j] + (X.mp[i][k] * Y.mp[k][j] % m)) % m; 
			}
		}
	}
	return res; 
}
void qpow(MATRIX a, int p) {
	while(p) {
		if(p & 1) Ans = Ans * a; 
		a = a * a; 
		p >>= 1; 
	} 
	return; 
}
signed main(){
	read(p), read(q), read(a1), read(a2), read(n), read(m);
    if(n == 1) return !printf("%lld", a1);  
    if(n == 2) return !printf("%lld", a2); 
	A.mp[1][1] = p, A.mp[1][2] = 1, A.mp[2][1] = q, A.mp[2][2] = 0; 
	Ans.mp[1][1] = a2, Ans.mp[1][2] = a1; 
	qpow(A, n - 2); 
	write(Ans.mp[1][1] % m); 
	return 0;
}