斐波那契前 n 项和

斐波那契前 n 项和

大家都知道 Fibonacci 数列吧,f1=1,f2=1,f3=2,f4=3,,fn=fn1+fn2

现在问题很简单,输入 nm,求 fn 的前 n 项和 Snmodm

输入格式

共一行,包含两个整数 nm

输出格式

输出前 n 项和 Snmodm 的值。

数据范围

1n2000000000,
1m1000000010

输入样例:

5 1000

输出样例:

12

 

解题思路

  遇到递推的题目想想能不能构造出矩阵,然后通过矩阵乘法和快速幂来快速求出第n项是什么。

  设斐波那契的第n项为fn,定义向量Fn=[fnfn+1],那么Fn+1=[fn+1fn+2]。尝试构造一个2×2的矩阵A使得Fn×A=Fn+1。容易发现[fnfn+1]×[0111]=[fn+1fn+2]

  因此A=[0111]。同时根据矩阵乘法的结合律,有Fn=F1×An1An1可以用快速幂来算。

  现在的问题是求斐波那契的前n项和Sn=i=1nfn。那么我们构造向量Fn=[fnfn+1Sn],继续找到一个3×3的矩阵A使得Fn×A=Fn+1。同样有[fnfn+1Sn]×[010111001]=[fn+1fn+2Sn+1]

  那么A=[010111001]Fn=F1×An1,其中F1=[111]

  AC代码如下,时间复杂度为O(33×logn)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n, m;
 5 int a[3][3] = {
 6     {0, 1, 0},
 7     {1, 1, 1},
 8     {0, 0, 1}
 9 };
10 
11 void mul(int c[], int a[], int b[][3]) {    // c = a * b
12     int tmp[3] = {0};
13     for (int i = 0; i < 3; i++) {
14         for (int j = 0; j < 3; j++) {
15             tmp[i] = (tmp[i] + 1ll * a[j] * b[j][i]) % m;
16         }
17     }
18     memcpy(c, tmp, sizeof(tmp));
19 }
20 
21 void mul(int c[][3], int a[][3], int b[][3]) {  // c = a * b
22     int tmp[3][3] = {0};
23     for (int i = 0; i < 3; i++) {
24         for (int j = 0; j < 3; j++) {
25             for (int k = 0; k < 3; k++) {
26                 tmp[i][j] = (tmp[i][j] + 1ll * a[i][k] * b[k][j]) % m;
27             }
28         }
29     }
30     memcpy(c, tmp, sizeof(tmp));
31 }
32 
33 int main() {
34     cin >> n >> m;
35     int f1[3] = {1, 1, 1};
36     n--;
37     while (n) {
38         if (n & 1) mul(f1, f1, a);  // f1 = f1 * a
39         mul(a, a, a);   // a = a * a
40         n >>= 1;
41     }
42     cout << f1[2];
43     
44     return 0;
45 }
复制代码

  为了方便统一成两个矩阵的乘法,可以把F1扩展为3×3的矩阵F1=[111000000]

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n, m;
 5 int a[3][3] = {
 6     {0, 1, 0},
 7     {1, 1, 1},
 8     {0, 0, 1}
 9 };
10 
11 void mul(int c[][3], int a[][3], int b[][3]) {
12     int tmp[3][3] = {0};
13     for (int i = 0; i < 3; i++) {
14         for (int j = 0; j < 3; j++) {
15             for (int k = 0; k < 3; k++) {
16                 tmp[i][j] = (tmp[i][j] + 1ll * a[i][k] * b[k][j]) % m;
17             }
18         }
19     }
20     memcpy(c, tmp, sizeof(tmp));
21 }
22 
23 int main() {
24     cin >> n >> m;
25     int f1[3][3] = {1, 1, 1};
26     n--;
27     while (n) {
28         if (n & 1) mul(f1, f1, a);
29         mul(a, a, a);
30         n >>= 1;
31     }
32     cout << f1[0][2];
33     
34     return 0;
35 }
复制代码

 

参考资料

  AcWing 1303. 斐波那契前 n 项和(算法提高课):https://www.acwing.com/video/704/

posted @   onlyblues  阅读(134)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-01-28 圆形牛棚
Web Analytics
点击右上角即可分享
微信分享提示