递推·矩阵加速

这个题其实很简单,简单分析一下规律,发现发f[i]=f[i-1]+f[i-2]。

如下图:

程序:

 1 #include<iostream>
 2 using namespace std;
 3 int main()
 4 {
 5   int n,i,j,a[101];
 6   cin>>n;
 7   a[1]=1;a[2]=2;
 8   for (i=3;i<=n;i++)
 9    {
10      a[i]=a[i-1]+a[i-2];
11     }
12 cout<<a[n];
13 } 

用这个代码,解决这个题的确很轻松。

可是只要稍微更改一下数据范围,就完全不一样了:

这样子,难度就完全不是一个等级了。

首先是不能开一个1000000000000000000的数组,那样肯定会爆内存。

我们可以用滚动的数组:

 1  1 #include<bits/stdc++.h>
 2  2 #define mod 1e9+7
 3  3
 4  4 using namespace std;
 5  5
 6  6 long long a[4] = {0,1,2};
 7  7
 8  8 int main()
 9  9 {
10 10     freopen("brick.in", "r", stdin);
11 11     freopen("brick.out", "w", stdout);
12 12     int fbk;
13 13     cin >> fbk;
14 14     if (fbk==1)
15 15     {
16 16         cout << 1;
17 17     }
18 18     else if (fbk==2)
19 19     {
20 20         cout << 2;
21 21     }
22 22     else
23 23     {
24 24         for (int yousa = 3; yousa <= fbk-2; yousa++)
25 25         {
26 26             a[3] = (a[2] + a[1])%(long long)(mod);
27 27             a[1] = a[2];
28 28             a[2] = a[3];
29 29         }
30 30         cout << a[3] << "\n";
31 31     }
32 32
33 33     fclose(stdin);
34 34     fclose(stdout);
35 35 }

测试之后发现虽然内存占用很少,但是会超时。

这就用到我们的矩阵加速了:

矩阵乘法

矩阵乘法可以先稍作了解,知道矩阵相乘的运算法则

C[i][j]=∑k=1k=5A[i][k]∗B[k][j]

快速幂

快速幂要求解的是这样一类问题:

给你A,B,C,求A的B次方模C的余数

A,C<=10^9,B<=10^18

如果我们线性去求,时间复杂度是O(n)的,但题目中给出的B是很大的数,这样显然会超时,我们可以用快速幂来加速这个过程。

我们可以想像一下小学的时候我们如何计算2^16

2^16=4^8=16^4=256^2=65536

那如何计算2^18呢?

2^18=4^9=4∗4^8=4∗16^4=4∗256^2=4∗65536=262144

快速幂同理也是如此

我们可以按照上面做法,利用分治的思想求去解

这样原本O(n)的时间复杂度便降到了O(log n )

1 long long ans=1,base=a;
2 while(n>0){
3         if(n&1){
4             ans*=base;
5         }
6         base*=base;
7         n=n/2;
8     }

矩阵快速幂

矩阵快速幂的原理同快速幂一样,只是转换为了矩阵之间的乘法操作

所以单纯的重载一下运算符(写成函数的形式也可),将普通的乘法转换为矩阵乘法就好了。

矩阵加速

知道那个叫矩阵快速幂的东西后我们可以学矩阵加速了

斐波那契数列中的每一项都是前两项之和

我们考虑构造这么一个矩阵:每一次乘上这个矩阵都能从f[n-1],f[n-2]两项向后递推到f[n-1],f[n]这两项

那么关键就是如何构造这样的矩阵

(1  1

1   0)

对于这样一个矩阵我们有

(f[n−1]f[n−2])∗(1110)=(f[n]f[n−1])(f[n−1]f[n−2])∗(1110)=(f[n]f[n−1])

所以我们将每一次两项相加转换为了乘以一个转移矩阵

既然是乘法,每次乘以的也是同一个矩阵

我们可以利用矩阵快速幂的思想对于求解斐波那契数列加速

代码实现基本上是一致的,只需要构造一个转移矩阵来进行状态之间的转移即可

 1 struct mat{
 2     ll m[5][5];
 3 }a,ans;
 4 ll n,b,k;
 5 mat mul(mat x,mat y,int flag){
 6     mat c;
 7     for(int i=1;i<=2;i++)
 8         for(int j=1;j<=2;j++)
 9             c.m[i][j]=0;
10     for(int i=1;i<=2;i++){
11         for(int j=1;j<=2;j++){
12             for(int q=1;q<=2;q++){
13                     c.m[i][j]=(c.m[i][j]+x.m[i][q]*y.m[q][j])%Mod;
14
15             }
16         }
17     }
18     return c;
19 }
20 int main(){
21     cin >> n;
22     a.m[1][1]=1;a.m[1][2]=1;
23     a.m[2][1]=1;a.m[2][2]=0;
24     b=n-2;
25     ans.m[1][1]=1;
26     ans.m[1][2]=1;
27     while(b){
28         if(b&1){
29             ans=mul(ans,a,1);
30         }
31         a=mul(a,a,2);
32         b=b/2;
33     }
34     if(n==1||n==2)cout<<1;
35     else cout<<ans.m[1][1]%Mod;
36 }

这样这个题就被解决了!

原文地址:https://www.cnblogs.com/arknight/p/12400709.html

时间: 2024-08-04 18:28:12

递推·矩阵加速的相关文章

2014 BNU 邀请赛E题(递推+矩阵快速幂)

Elegant String 题意:给定一个字符串,由0-k数字组成,要求该串中,子串不包含0-k全排列的方案数 思路:dp[i][j]表示放到i个数字,后面有j个不相同,然后想递推式,大概就是对应每种情况k分别能由那几种状态转移过来,在纸上画画就能构造出矩阵了,由于n很大,所以用快速幂解决 代码: #include <stdio.h> #include <string.h> const long long MOD = 20140518; int t; long long n; i

HDU 5950 Recursive sequence 【递推+矩阵快速幂】 (2016ACM/ICPC亚洲区沈阳站)

Recursive sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 249    Accepted Submission(s): 140 Problem Description Farmer John likes to play mathematics games with his N cows. Recently, t

hdu 2604 递推 矩阵快速幂

HDU 2604 Queuing (递推+矩阵快速幂) 这位作者讲的不错,可以看看他的 #include <cstdio> #include <iostream> #include <algorithm> #include <cmath> #include <cstring> using namespace std; const int N = 5; int msize, Mod; struct Mat { int mat[N][N]; }; M

[递推+矩阵快速幂]Codeforces 1117D - Magic Gems

传送门:Educational Codeforces Round 60 – D 题意: 给定N,M(n <1e18,m <= 100) 一个magic gem可以分裂成M个普通的gem,现在需要N个gem,可以选择一定的magic gem,指定每一个分裂或不分裂,问一共有多少种方案 两种分裂方案不同当且仅当magic gem的数量不同,或者分裂的magic gem的索引不同. 思路: 1.首先从dp的角度出发 设F(i)为最终需要i个gem的方案数,容易得到递推式: (总方案数 = 最右边的m

HDU 2604 Queuing (递推+矩阵快速幂)

[题目链接]:click here~~ [题目大意]: n个人排队,f表示女,m表示男,包含子串'fmf'和'fff'的序列为O队列,否则为E队列,有多少个序列为E队列. [思路]: 用f(n)表示n个人满足条件的结果,那么如果最后一个人是m的话,那么前n-1个满足条件即可,就是f(n-1): 如果最后一个是f那么这个还无法推出结果,那么往前再考虑一位:那么后三位可能是:mmf, fmf, mff, fff,其中fff和fmf不满足题意所以我们不考虑,但是如果是 mmf的话那么前n-3可以找满足

HDU2604-Queuing(递推+矩阵快速幂)

题目链接 题意:男为f,女为m,求在长度为L的队列中不存在fmf,fff这样子序列的序列的个数. 思路:又是递推题,假设长度为L的队列中存在的序列个数为f(L),那么考虑最后一个放的字母,假设最后一个放m,那么前L-1个可以随意排列,即个数为f(L - 1):如果最后一个放f,那么考虑后两个字母,可能出现的情况为ff,mf,这样比较难判断是否符合题目要求的,所以我们考虑后三个字母,可能出现的情况就为fff,mff,fmf,mmf,显而易见mff,mmf符合题意.当后三个为mmf时,前L-3个可以

HDU 2842 (递推+矩阵快速幂)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2842 题目大意:棒子上套环.第i个环能拿下的条件是:第i-1个环在棒子上,前i-2个环不在棒子上.每个环可以取下或放上,cost=1.求最小cost.MOD 200907. 解题思路: 递推公式 题目意思非常无聊,感觉是YY的. 设$dp[i]$为取第i个环时的总cost. $dp[1]=1$,$dp[2]=2$,前两个环取下是没有条件要求的. 从i=3开始,由于条件对最后的环限制最大,所以从最后一

2017中国大学生程序设计竞赛 - 女生专场 Happy Necklace(递推+矩阵快速幂)

Happy Necklace Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 1146    Accepted Submission(s): 491 Problem Description Little Q wants to buy a necklace for his girlfriend. Necklaces are single

HDU6185 Covering (递推+矩阵快速幂)

大致题意:让你用1*2规格的地毯去铺4*n规格的地面,告诉你n,问有多少种不同的方案使得地面恰好被铺满且地毯不重叠.答案对1000000007取模 递推得f(n)=f(n-1)+5*f(n-2)+f(n-3)-f(n-4),因为n很大,所以接下来用矩阵快速幂搞搞就可以了. #include <iostream> #include <cstring> #include <cstdio> using namespace std; long long n; long long