1. 程式人生 > >各種斐波那契矩陣乘法快速冪

各種斐波那契矩陣乘法快速冪

T1

f[1]=1; f[2]=1; f[n]=f[n-1]+f[n-2];
求f[n]

n<2^32

思路

暴力顯然不行。
現在需要一種更強的方法:矩陣乘法。

考慮矩陣[f[n-1],f[n]]*A=[f[n],f[n-1]+f[n]]
推出矩陣A
[0,1]
[1,1]

因為矩陣乘法滿足結合律
所以[f[1],f[2]]*A^n-1=[f[n-1],f[n]]

可用快速冪求出A^n-1

那麼水就不貼程式碼了。

T2

f[1]=1; f[2]=1; f[n]=f[n-1]+f[n-2]+1;
求f[n]

思路

考慮矩陣[f[n-1],f[n],1]*A=[f[n],f[n-1]+f[n]+1,1]

得矩陣A
[0,1,0]
[1,1,0]
[0,1,1]

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int x=3,mod=9973;
int a[x][x]={{0,1,0},{1,1,0},{0,1,1}},b[x]={1,1,1},c[x][x],n;
void power(int t)
{
    if(t==1) return;
    int d[x][x];
    memset(d,0,sizeof(d));
    for
(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) d[i][j]=a[i][j]; memset(c,0,sizeof(c)); for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) { for(int k=0; k<x; k++) c[i][j]=(c[i][j]+a[i][k]*a[k][j])%mod; } for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) a[i][j]=c[i][j]; memset
(c,0,sizeof(c)); power(t/2); memset(c,0,sizeof(c)); if(t%2) { for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) { for(int k=0; k<x; k++) c[i][j]=(c[i][j]+a[i][k]*d[k][j])%mod; } for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) a[i][j]=c[i][j]; } } int main() { scanf("%d",&n); n--; power(n); memset(c,0,sizeof(c)); for(int j=0; j<=x-1; j++) for(int k=0; k<=x-1; k++) { c[0][j]=(c[0][j]+b[j]*a[k][j])%mod; } printf("%d",c[0][0]); }

T3

求數列f[n]=f[n-2]+f[n-1]+n+1的第N項,其中f[1]=1,f[2]=1.

思路

其實也差不多

考慮矩陣[f[n-1],f[n],n+1,1]*A=[f[n],f[n-1]+f[n]+1+n,n+1,1]

得A

[0,1,0,0]
[1,1,0,0]
[0,1,1,0]
[0,1,1,1]

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int x=4,mod=9973;
int a[x][x]={{0,1,0,0},{1,1,0,0},{0,1,1,0},{0,1,1,1}},b[x]={1,1,3,1},c[x][x],n;
void power(int t)
{
    if(t==1) return;
    int d[x][x];
    memset(d,0,sizeof(d));
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) d[i][j]=a[i][j];
    memset(c,0,sizeof(c));
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++)
    {
        for(int k=0; k<x; k++) c[i][j]=(c[i][j]+a[i][k]*a[k][j])%mod;
    }
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) a[i][j]=c[i][j];
    memset(c,0,sizeof(c));
    power(t/2);
    memset(c,0,sizeof(c));
    if(t%2)
    {
        for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++)
        {
            for(int k=0; k<x; k++) 
                c[i][j]=(c[i][j]+a[i][k]*d[k][j])%mod;
        }
        for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) a[i][j]=c[i][j];
    }
}
int main()
{
    scanf("%d",&n);
    n--;
    power(n);
    memset(c,0,sizeof(c));
    for(int j=0; j<=x-1; j++) for(int k=0; k<=x-1; k++)
    {
        c[0][j]=(c[0][j]+b[k]*a[k][j])%mod;
    }
    printf("%d",c[0][0]);
}



T4

某天,幼兒園學生LZH周測數學時嚇哭了,一道題都做不出來。這下可麻煩了他馬上就會成為墊底的0分啊。他的期望也不高,做出最簡單的第一題就夠了
題目是這樣的,定義F(n)=((根號5+1)/2)^(n-1) ,當然為了凸顯題目的簡單當然不能是小數分數或無理數,F(x)因此需要向上取整,當然求F(n)是非常難的!因此幼兒園園長頭皮決定簡單一點,求下F(x)的前n項和就行了。

題目大意

數列f[n]=f[n-1]+f[n-2],f[1]=f[2]=1的前n項和s[n]的快速求法

思路

設sigma(i=1,i<=n,f[i])為s[n]

考慮矩陣[f[n-1],f[n],s[n-1]]*A=[f[n],f[n-1]+f[n],s[n-1]+f[n]]

得A

[0,1,0]
[1,1,1]
[0,0,1]

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int x=3,mod=1000000007;
long long a[x][x]={{0,1,0},{1,1,1},{0,0,1}},b[x]={1,1,1},c[x][x],n;
void power(int t)
{
    if(t<=1) return;
    int d[x][x];
    memset(d,0,sizeof(d));
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) d[i][j]=a[i][j];
    memset(c,0,sizeof(c));
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++)
    {
        for(int k=0; k<x; k++) c[i][j]=(c[i][j]+a[i][k]*a[k][j])%mod;
    }
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) a[i][j]=c[i][j];
    memset(c,0,sizeof(c));
    power(t/2);
    memset(c,0,sizeof(c));
    if(t%2)
    {
        for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++)
        {
            for(int k=0; k<x; k++) 
                c[i][j]=(c[i][j]+a[i][k]*d[k][j])%mod;
        }
        for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) a[i][j]=c[i][j];
    }
}
int main()
{
    scanf("%d",&n);
    n--;
    if(!n)
    {
        printf("1");return 0;
    }
    power(n);
    memset(c,0,sizeof(c));
    for(int j=0; j<=x-1; j++) for(int k=0; k<=x-1; k++)
    {
        c[0][j]=(c[0][j]+b[k]*a[k][j])%mod;
    }
    printf("%d",c[0][2]);
}


T5

這天,當一頭霧水的LZH同學在考場上痛哭的時候,一旁的YMW早就如切菜一樣cut掉了簡單至極的第一題,風輕雲淡的衝擊著滿分,然而最後一道題著實難道了他,畢竟是幼兒園副園長樹皮和著名毒瘤秋彪為了防止人AK而出的,可是YMW作為ACrush的著名粉絲,向來以AK為目標,永不言敗,而他能不能AK就看你了
題目是醬紫的,f(n)-f(3)-f(4)-f(5)-…-f(n-3)-f(n-2)=(n+4)(n-1)/2,f(1)=1,f(2)=1
求f(n)的前n項和

題目大意

數列f[n]=f[n-1]+f[n-2]+n+1,f[1]=f[2]=1的前n項和s[n]的快速求法

思路

[f[n-1],f[n],s[n-1],n+1,1]*A=[f[n],f[n-1]+f[n]+n+1,s[n-1]+f[n],n+1,1]

A=
[0,1,0,0,0]
[1,1,1,0,0]
[0,0,1,0,0]
[0,1,0,1,0]
[0,1,0,1,1]

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int x=5,mod=1000000007;
long long a[x][x]={{0,1,0,0,0},
                   {1,1,1,0,0},
                   {0,0,1,0,0},
                   {0,1,0,1,0},
                   {0,1,0,1,1}},b[x]={1,1,1,3,1},c[x][x],n;
void power(int t)
{
    if(t<=1) return;
    int d[x][x];
    memset(d,0,sizeof(d));
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) d[i][j]=a[i][j];
    memset(c,0,sizeof(c));
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++)
    {
        for(int k=0; k<x; k++) c[i][j]=(c[i][j]+a[i][k]*a[k][j])%mod;
    }
    for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) a[i][j]=c[i][j];
    memset(c,0,sizeof(c));
    power(t/2);
    memset(c,0,sizeof(c));
    if(t%2)
    {
        for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++)
        {
            for(int k=0; k<x; k++) 
                c[i][j]=(c[i][j]+a[i][k]*d[k][j])%mod;
        }
        for(int i=0; i<=x-1; i++) for(int j=0; j<=x-1; j++) a[i][j]=c[i][j];
    }
}
int main()
{
    scanf("%d",&n);
    n--;
    if(!n)
    {
        printf("1");return 0;
    }
    power(n);
    memset(c,0,sizeof(c));
    for(int j=0; j<=x-1; j++) for(int k=0; k<=x-1; k++)
    {
        c[0][j]=(c[0][j]+b[k]*a[k][j])%mod;
    }
    printf("%d",c[0][2]);
}