矩阵快速幂:
首先我们来看如何快速地求出斐波那契数列的第n项
设 F n = [ f n , f n + 1 ] F_n = [f_n,f_{n+1}] Fn=[fn,fn+1],构造这一个行向量,那么对于此,我们思考 F n F_n Fn乘一个什么样的矩阵可以得到 F n + 1 F_{n+1} Fn+1
显然:可以乘一个这样子的矩阵
然后我们对这个递推的操作一直乘矩阵A,就可以推出最终 F n F_n Fn的表达式:
即 F n = F 0 ∗ A n F_n = F_0 * A^n Fn=F0∗An,求出 F n F_n Fn之后,自然就得到 f n f_n fn了!
这个其实具体是一个推公式的过程,没有涉及到其他的东西,
设 S n = f 0 + f 1 + f 2 + . . . + f n S_n = f_0 + f_1 + f_2 +...+f_n Sn=f0+f1+f2+...+fn
具体的公式为:
S n = f n + 2 − f 1 S_n = f_{n+2} - f_1 Sn=fn+2−f1 注意在我规定的斐波那契数列中,是从第0项开始的,公式具体的推导如下:
因此若要求斐波那契数列的前n项和,只需要求出 f n + 2 f_{n+2} fn+2即可,那么这个问题其实又回到了求斐波那契数列的第n项,这样来看就很简单啦!
构造一个新的行向量,
设 F n = [ f n , f n + 1 , S n ] F_n = [f_n,f_{n+1},S_n] Fn=[fn,fn+1,Sn] ,这里的 S n S_n Sn就表示的是前n项的和,那么我们还是像开始一样思考一下如何构造矩阵,使得 F n F_n Fn可以通过递推来求得。很容易推导出矩阵A的形式应该为:
因此我们可以通过此递推得到: F n = F 0 ∗ A n F_n = F_0 * A^{n} Fn=F0∗An
这样只要我们把 F n F_n Fn求出来,就可以得到结果啦!
[1303. 斐波那契前 n 项和 - AcWing题库]:
附上AC代码:
#include
#include
#include
using namespace std;
typedef long long LL;
LL n,m;
const int N = 3;
int a[N][N] = {
{0,1,0},
{1,1,1},
{0,0,1},
};
// 矩阵乘法:计算行向量 * 一个矩阵
void mul(int c[],int a[],int b[][N]){ // 计算 a[] * b[][] -> 即:f1[] * a[][] 之后的值
int tmp[N] = {0}; // 表示最后的答案数组
for(int i = 0;i < N;i++){ // i表示答案的那一列
// c[i] 表示的值应该等于 a中的每一个数 * b中第i列的那个数 的求和
for(int j = 0;j < N;j++){ // j枚举的就是a中的每一个数
tmp[i] = (tmp[i] + (LL)a[j] * b[j][i]) % m;
}
}
memcpy(c,tmp,sizeof(tmp));
}
// 矩阵乘法:计算一个矩阵 * 一个矩阵
void mul(int c[][N],int a[][N],int b[][N]){
int tmp[N][N] = {0};
for(int i = 0;i < N;i++){
for(int j = 0;j < N;j++){ // 枚举计算tmp[i][j]
// 计算c[i][j]需要枚举的就是a[i][]的那一行的数 * b[][j]那一列的数
for(int k = 0;k < N;k++){
tmp[i][j] = (tmp[i][j] + (LL)a[i][k] * b[k][j]) % m;
}
}
}
memcpy(c,tmp,sizeof(tmp));
}
int main()
{
cin >> n >> m;
int f1[N] = {1,1,1}; // 表示的是 [f1,f2,S1]
// Fn为向量
// Fn = F1 * A^n-1 次幂
LL k = n - 1;
// 写快速幂
while(k){
if(k & 1) mul(f1,f1,a); // f1[] = f1[] * a[][]
k >>= 1;
mul(a,a,a); // a[][] = a[][] * a[][]
}
cout << f1[2] << endl;
return 0;
}
如何快速地求出 f 0 2 + f 1 2 + . . . + f n 2 f_0^2 + f_1^2 + ... + f_n^2 f02+f12+...+fn2 呢?
先给出结论: f 0 2 + f 1 2 + . . . + f n 2 = f i ∗ f i + 1 f_0^2 + f_1^2 + ... + f_n^2 = f_i * f_{i + 1} f02+f12+...+fn2=fi∗fi+1
下面给出证明的过程:
其实只要知道了结论,具体做起题目来就简单多了!
http://csoj.scnu.edu.cn/problem/S0413
附上AC代码:
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 2,mod = 1e9+7;
int a[N][N] = {
{0,1},
{1,1},
};
int f0[N];
int backup[N][N];
int t;
int n;
int l1,r1,l2,r2;
void mul(int c[][N],int a[][N],int b[][N]){
int tmp[N][N] = {0};
for(int i = 0;i < N;i++){
for(int j = 0;j < N;j++){
for(int k = 0;k < N;k++){
tmp[i][j] = (tmp[i][j] + (LL)a[i][k] * b[k][j]) % mod;
}
}
}
memcpy(c,tmp,sizeof(tmp));
}
void mul(int c[],int a[],int b[][N]){
int tmp[N] = {0};
for(int i = 0;i < N;i++){
for(int j = 0;j < N;j++){
tmp[i] = (tmp[i] + (LL)a[j] * b[j][i]) % mod;
}
}
memcpy(c,tmp,sizeof(tmp));
}
LL F(int x){ // 求Fx Fn = F0 * A^n次幂
memcpy(a,backup,sizeof(backup));
f0[0] = f0[1] = n;
while(x){
if(x & 1) mul(f0,f0,a);
x >>= 1;
mul(a,a,a);
}
return (LL)f0[0];
}
LL qmi(LL a,LL k,LL p){
LL res = 1;
while(k){
if(k & 1) res = res * a % p;
k >>= 1;
a = a * a % p;
}
return res;
}
int main()
{
cin >> n;
int t;
cin >> t;
memcpy(backup,a,sizeof(a));
while(t--){
cin >> l1 >> r1 >> l2 >> r2;
// cout << F(l1 - 1) << " " << F(l1) << " " << F(r1) << " " << F(r1+1) << endl;
// cout << F(r2 + 2) << " " << F(l2 + 1) << endl;
LL fenzi = ((F(r1) * F(r1 + 1) % mod - F(l1 - 1) * F(l1) % mod) % mod + mod) % mod;
// cout << "fenzi = " << fenzi << endl;
LL fenmu = ((F(r2 + 2) - F(l2 + 1)) % mod + mod) % mod;
// 然后求逆元
LL fenmu2 = qmi(fenmu,mod-2,mod);
// cout << "分母 = " << fenmu2 << endl;
cout << fenzi * fenmu2 % mod << endl;
}
return 0;
}