COJ 1208 Fibonacci sum

题意: 求Fibonacci数列前N项的K次方和。

分析: 这种题明显是用矩阵快速幂解决 ,问题是怎样找出递推方程。

由Fibonacci数列的性质知: f(i)  = f(i-1) + f(i-2) ;       

f(a)^k = [f(a-1) + f(a-2)]^k

二项式展开有个很好的性质:展开后与原式保持齐次。 正好可以用在这里。

f(a)^x * f(a-1)^(k-x)    =     sum{ C(x , i) *  f(a-1)^(k+i-x)  *  f(a-2)^(x-i)     |   ( 0 <=  i  <= x ) }   可以看到始终是K次。


这样就容易得到递推公式, 即求f(a)^k方法。

求前n项和,有两种方法:

① 二分矩阵求和

② 将和加入递推公式中。

明显②要高效的得多 ,实测 ①800+ms ②40+ms


参考代码:

// 二分矩阵
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int maxn = 21 ;
const LL MOD = 1000000007LL;
int K ;
struct Mat{
    int m[maxn][maxn] ;
    Mat(int a=0){
        memset(m , 0 ,sizeof(m)) ;
        for(int i=0; i<=K; i++) m[i][i] = a ;
    }
};
 
Mat operator + (const Mat &a ,const Mat &b){
    Mat ret;
    for(int i=0; i<=K; i++)
        for(int j=0; j<=K ; j++)
            ret.m[i][j] = ((LL)a.m[i][j] + b.m[i][j]) % MOD ;
    return ret ;
}
Mat operator * (const Mat &a ,const Mat &b){
    Mat ret ;
    for(int i=0; i<=K; i++)
        for(int j=0; j<=K; j++){
            unsigned long long tmp = 0;
            for(int k=0; k<=K; k++){
                tmp += (LL)a.m[i][k] * b.m[k][j] ;
                if(k==10) tmp %= MOD ;
            }
            ret.m[i][j] = tmp%MOD ;
        }
    return ret ;
}
Mat operator ^ (Mat a, int n){
    Mat ret(1) ;
    while(n){
        if(n&1) ret = ret * a ;
        a = a*a ;
        n>>=1 ;
    }
    return ret ;
}
Mat sum(Mat a , int n){
    Mat E(1) ;
    if(n == 0) return E ;
    if(n&1) return ((a^(n/2+1)) + E) * sum(a , n/2) ;
    else {
        Mat  tmp = a^(n/2);
        return tmp + (tmp*a+E)*sum(a , n/2-1);
    }
}
 
Mat A ;
LL C[30][30] ;
 
void prepare(){
    C[0][0] = 1 ;
    for(int i=1; i<25; i++){
        C[i][0] = 1 ;
        for(int j=1; j<25; j++){
            C[i][j] += C[i-1][j-1] + C[i-1][j] ;
            while(C[i][j] >= MOD) C[i][j] -= MOD ;
        }
    }
}
 
int solve(int n){
    if( n == 1) { return 1 ;}
    memset(A.m ,0 ,sizeof(A.m)) ;
    for(int i=0; i<=K ;i++){
        for(int j=0; j<=i; j++)
            A.m[i][K-i+j] = C[i][j] ;
    }
    A = sum(A , n-2);
    LL ans = 0;
    for(int i=0; i<=K; i++) ans += A.m[K][i] ;
    ans++ ;
    while(ans >= MOD) ans-=MOD;
    return ans ;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    prepare() ;
    int T  , n ;
    scanf("%d" , &T);
    while(T--){
        scanf("%d%d" , &n ,&K);
        int ans = solve(n) ;
        printf("%d\n" , ans) ;
    }
    return 0;
}


//② 将和也拉入递推矩阵
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int maxn = 22 ;
const LL MOD = 1000000007LL;
int K ;
struct Mat{
    int m[maxn][maxn] ;
    Mat(int a=0){
        memset(m , 0 ,sizeof(m)) ;
        for(int i=0; i<=K+1; i++) m[i][i] = a ;
    }
};
 
 
Mat operator * (const Mat &a ,const Mat &b){
    Mat ret ;
    for(int i=0; i<=K+1; i++)
        for(int j=0; j<=K+1; j++){
            unsigned long long tmp = 0;
            for(int k=0; k<=K+1; k++){
                tmp += (LL)a.m[i][k] * b.m[k][j] ;
                if(k==10) tmp %= MOD ;
            }
            ret.m[i][j] = tmp%MOD ;
        }
    return ret ;
}
Mat operator ^ (Mat a, int n){
    Mat ret(1) ;
    while(n){
        if(n&1) ret = ret * a ;
        a = a*a ;
        n>>=1 ;
    }
    return ret ;
}
 
Mat A ;
LL C[30][30] ;
 
void prepare(){
    C[0][0] = 1 ;
    for(int i=1; i<25; i++){
        C[i][0] = 1 ;
        for(int j=1; j<25; j++){
            C[i][j] += C[i-1][j-1] + C[i-1][j] ;
            while(C[i][j] >= MOD) C[i][j] -= MOD ;
        }
    }
}
 
int solve(int n){
    if( n == 1) { return 1 ;}
    memset(A.m ,0 ,sizeof(A.m)) ;
    for(int i=0; i<=K ;i++){
        for(int j=0; j<=i; j++)
            A.m[i][K-i+j] = C[i][j] ;
    }
    A.m[K+1][K] = A.m[K+1][K+1] = 1;
    A = A^(n-1) ;
    LL ans = 0;
    for(int i=0; i<=K+1; i++) ans += A.m[K+1][i] ;
    while(ans >= MOD) ans-=MOD;
    return ans ;
}
int main()
{
    prepare() ;
    int T  , n ;
    scanf("%d" , &T);
    while(T--){
        scanf("%d%d" , &n ,&K);
        int ans = solve(n) ;
        printf("%d\n" , ans) ;
    }
    return 0;
}
 



你可能感兴趣的:(COJ 1208 Fibonacci sum)