斐波那契矩阵快速幂(更新中)

首先附上一张当遇上关于矩阵快速幂时候能够很方便套用的拓展公式(从师姐哪里拿来哒)

之所以要用矩阵快速幂是因为快啊!!快啊!!

为了解决幂次很大时候会超时的问题

 

首相以最基础的题目开始讲解

poj-3070 

 1 ///关于Fibonacci最基础的题目
 2 #include<stdio.h>
 3 #define mod 10000
 4 int n;
 5 long long res[2][2];
 6 void mul(long long a[][2],long long b[][2]){
 7     long long tmp[2][2];
 8 for(int i=0;i<2;i++)
 9     for(int j=0;j<2;j++){
10     tmp[i][j]=0;
11     for(int k=0;k<2;k++){
12     tmp[i][j]+=a[i][k]*b[k][j]%mod;
13      if(tmp[i][j]>=mod)
14         tmp[i][j]-=mod;
15         }
16   }
17   for(int i=0;i<2;i++)
18     for(int j=0;j<2;j++)
19     a[i][j]=tmp[i][j];
20 }
21 void pow(long long a[][2]){
22      for(int i=0;i<2;i++)
23         for(int j=0;j<2;j++)
24         res[i][j]=(i==j);
25      while(n){
26         if(n&1) mul(res,a);
27         n>>=1;
28         mul(a,a);
29      }
30 }
31 int main(){
32     while(1){
33         scanf("%d",&n);
34         if(n==-1)
35             break;
36             if(n==0){
37                 printf("0\n");
38                 continue;
39             }
40             else if(n==1){
41                 printf("1\n");
42                 continue;
43             }
44         long long ans[2][2]={1,1,1,0};
45         n--;
46         pow(ans);
47         long long a[2]={1,0};
48         long long sum=0;
49         for(int i=0;i<2;i++){
50                 sum+=res[0][i]*a[i]%mod;
51                 if(sum>=mod)
52                     sum-=mod;
53             }
54             printf("%lld\n",sum);
55     }
56 return 0;
57 }
View Code

 

题意就是求F(n) (0<=n<=1000 000 000)

递推式 F(n)=F(n-1)+F(n-2)  (n>=2)

           F(n)=1  (n==1)

           F(n)=0  (n==0)

Time Limit: 1000MS ,在多测试用例的情况下,n能够达到1000 000 000,如果每次都乖乖地递归下去肯定会tle的!

所以此时我们要利用到Fibonacci矩阵快速幂

什么是快速幂?

快速幂就是利用二进制来加快计算

现求an,n==47(二进制:101111)

如果慢慢地一次次乘的话就需要47次

如果利用47的二进制(101111)应该用多少次呢?现在我们来计算一下,首先假设初始的ans=1,每次都右移一位并且a=a*a,每当末位是1的时候,ans=ans*a。

令A=a

1.ans=1,因为101111&1==1 ,ans=ans*A=a ,右移一位成10111,A=A*A=a2

2.ans=a,因为10111&1==1   ,ans=ans* A=a*a2=a3, 右移一位成1011 ,A=A*A=a4

3.ans=a3,因为1011&1==1   ,ans=ans*A=a3*a4=a7,右移一位成101 ,A=A*A=a8

4.ans=a7,因为101&1==1    ,ans=ans*A=a7*a8=a15,右移一位成10  ,A=A*A=a16

5.ans=a15,因为10&1==0    ,对ans不做任何操作     ,右移一位成1,A=A*A=a32

6.ans=a15,因为1&1==1    ,ans=ans*A=a15*a32=a47,右移一位成0(end.)

显然如果用二进制快速幂的话只需6步即可。

对于一个long long 型的整数,最大的时候也就32位,所以最多只需要算32次,比你直接慢慢计算232次快太多了!

如何构造矩阵?(只要构造好矩阵,直接套进模板即可,网上说这是线性还是啥子方面的内容,由于我暂时还没学到,下面都是我遇到题目时候的个人总结)

对于递推式 F(n)=F(n-1)+F(n-2)  (n>=2)

                   F(n)=1  (n==1)

                   F(n)=0  (n==0)

现假设我们有a,b,c三个矩阵,a*b=c

1.首先我们先解决好b,c矩阵该如何构造,现在我要假设b矩阵是对应于F(n)递推式右边,c矩阵是对应于F(n+1)递推式右边(即F(n-1)+F(n-2)和F(n)+F(n-1)),所以我们是直接把F(n-1)+F(n-2)和F(n)+F(n-1)分别代进去b,c矩阵即可了吗?并不!还不一定构造完呢!由于递推式拓展式各式各样,还需要仔细思考一下!但对于递推式 F(n)=F(n-1)+F(n-2) 来说确实是已经构造好啦!

2.构造a矩阵,现由于步骤一所以我们已经知道了b,c矩阵了,只要满足a*b=c即可。另:a矩阵需满足行列都等于b,c矩阵的列,因此a矩阵:2x2。假设a矩阵每一行都有A,B两个数,则A1*F(n-1)+B1*F(n-2)=F(n),A2*F(n-1)+B2*F(n-2)=F(n-1),所以A1=1,B1=1,A2=1,B2=0 ,构造好a,b,c矩阵后再利用快速幂就可以很快求得答案啦!

3.矩阵的转化:现得到如上矩阵后还需要对它进行转化。已知n==1时等于1,n==0时等于0

n==2时:

n==3时:把n==2时得到的结果带入,得

n==4时:把n==3时得到的结果带入,得

纵观以上,可得,现在我们可以开始快速幂啦!^-^

 

又见斐波那契

 

给定递推式
求F(n)的值,由于这个值可能太大,请对109+7取模。
题目十分的简单直接,只要构造好矩阵方能ac(构造过程待更新)
 1 #include<stdio.h>
 2 #include<string.h>
 3 #define mod 1000000007
 4 long long n;
 5 int t;
 6 long long res[6][6];
 7 long long flag[6][6];
 8 void mul(long long a[][6],long long b[][6]){
 9 memset(flag,0,sizeof(flag));
10 for(int i=0;i<6;i++)
11     for(int j=0;j<6;j++)
12 for(int k=0;k<6;k++){
13     flag[i][j]+=a[i][k]*b[k][j]%mod;
14     if(flag[i][j]>=mod)
15         flag[i][j]-=mod;
16 }
17 for(int i=0;i<6;i++)
18     for(int j=0;j<6;j++)
19     a[i][j]=flag[i][j];
20 }
21 void pow(long long a[][6]){
22     memset(res,0,sizeof(res));
23 for(int i=0;i<6;i++)
24     for(int j=0;j<6;j++)
25             res[i][j]=(i==j);
26     while(n){
27         if(n&1) mul(res,a);
28         n=n>>1;
29         mul(a,a);
30     }
31 }
32 int main(){
33     scanf("%d",&t);
34     while(t--){
35        scanf("%lld",&n);
36        n--;
37      long long ans[6][6]={
38      1,1,1,1,1,1,
39      1,0,0,0,0,0,
40      0,0,1,3,3,1,
41      0,0,0,1,2,1,
42      0,0,0,0,1,1,
43      0,0,0,0,0,1
44      };
45      pow(ans);
46      long long sum=0;
47      long long x[6]={1,0,8,4,2,1};
48      for(int i=0;i<6;i++){
49          sum+=( res[0][i]*x[i])%mod;
50          if(sum>=mod)
51             sum-=mod;
52      }
53      printf("%lld\n",sum);
54     }
55 return 0;
56 }
View Code

generator 1

题目已经给出xi=axi1+bxi2 for all i≥2i \ge 2i2.递推式,这个题目在处理上和以往有点不同的就是求第n项答案时候这个n有可能会很大很大,所以要用到十进制处理的快速幂矩阵
 1 #include<stdio.h>
 2 #include<string.h>
 3 const int maxn=1e6+100;
 4 char s[maxn];
 5 int len;
 6 long long mod;
 7 long long res[2][2];
 8 void minus_one(){
 9     int p=len;
10 while(s[p]=='0'){
11     s[p]='9';
12     p--;
13 }
14 s[p]--;
15 }
16 void mul(long long a[][2],long long b[][2]){
17     long long tmp[2][2];
18     memset(tmp,0,sizeof(tmp));
19 for(int i=0;i<2;i++) for(int j=0;j<2;j++)
20 for(int k=0;k<2;k++){
21     tmp[i][j]+=a[i][k]*b[k][j]%mod;
22     if(tmp[i][j]>=mod)
23         tmp[i][j]-=mod;
24   }
25   for(int i=0;i<2;i++)
26     for(int j=0;j<2;j++)
27     a[i][j]=tmp[i][j];
28 }
29 void pow(long long a[][2]){
30     long long tmp[2][2];
31 res[1][1]=1,res[0][0]=1;
32 for(int k=len;k>0;k--){
33     int m=s[k]-'0';
34     for(int j=1;j<=m;j++)
35         mul(res,a);
36         for(int i=0;i<2;i++)
37             for(int j=0;j<2;j++)
38             tmp[i][j]=a[i][j];
39         for(int i=1;i<=3;i++)
40             mul(a,a);
41         mul(a,tmp);
42         mul(a,tmp);
43 }
44 }
45 int main(){
46     int x1,x2,a,b;
47     scanf("%d%d%d%d",&x2,&x1,&a,&b);
48     scanf("%s",s+1);
49     len=strlen(s+1);
50     minus_one();
51     scanf("%lld",&mod);
52     long long first[2][2]={a,b,1,0};
53     pow(first);
54     printf("%lld\n",(res[0][0]*x1%mod+res[0][1]*x2%mod)%mod);
55 return 0;
56 }
View Code

 hdu—5950

递推式 F(n)=F(n-1)+2*F(n-2)+n4.

 1 #include<stdio.h>
 2 #include<string.h>
 3 #define mod 2147493647
 4 long long n,a,b;
 5 int t;
 6 long long res[7][7];
 7 void mul(long long a[][7],long long b[][7]){
 8     long long tmp[7][7];
 9 for(int i=0;i<7;i++)
10     for(int j=0;j<7;j++){
11             tmp[i][j]=0;
12     for(int k=0;k<7;k++){
13     tmp[i][j]+=a[i][k]*b[k][j]%mod;
14     if(tmp[i][j]>=mod)
15         tmp[i][j]-=mod;
16          }
17     }
18     for(int i=0;i<7;i++)
19         for(int j=0;j<7;j++)
20         a[i][j]=tmp[i][j];
21 }
22 void pow(long long a[][7]){
23 for(int i=0;i<7;i++)
24     for(int j=0;j<7;j++)
25     res[i][j]=(i==j);
26 while(n){
27     if(n&1) mul(res,a);
28     n>>=1;
29     mul(a,a);
30 }
31 }
32 int main(){
33     scanf("%d",&t);
34     while(t--){
35         scanf("%lld%lld%lld",&n,&a,&b);
36         if(n==1){
37             printf("%lld\n",a);
38             continue;
39         }
40         else if(n==2){
41             printf("%lld\n",b);
42             continue;
43         }
44         n--;
45         n--;
46         long long ans[7][7]={
47         1,2,1,0,0,0,0,
48         1,0,0,0,0,0,0,
49         0,0,1,4,6,4,1,
50         0,0,0,1,3,3,1,
51         0,0,0,0,1,2,1,
52         0,0,0,0,0,1,1,
53         0,0,0,0,0,0,1
54         };
55         pow(ans);
56         long long flag[7]={b,a,81,27,9,3,1};
57         /*a[0]=b;
58         a[1]=a;
59         a[2]=16;
60         a[3]=8;
61         a[4]=4;
62         a[5]=2;
63         a[6]=1;*/
64         long long sum=0;
65         for(int i=0;i<7;i++){
66             sum+=res[0][i]*flag[i]%mod;
67             if(sum>=mod)
68                 sum-=mod;
69         }
70         printf("%lld\n",sum);
71     }
72 return 0;
73 }
View Code

 

 
posted @ 2019-07-26 19:20  HELLOAMMA  阅读(460)  评论(0编辑  收藏  举报