Number Sequence(HDU-1005)


1 题意

  已知 f 1 = f 2 = 1 f_1=f_2=1 f1=f2=1,求 f n = ( A f n − 1 + B f n − 2 )   m o d   7 f_n = (Af_{n - 1} + Bf_{n - 2}) \ mod \ 7 fn=(Afn1+Bfn2) mod 7,其中 1 ⩽ A , B ⩽ 1000 , 1 ⩽ n ⩽ 1 0 8 1 \leqslant A, B \leqslant 1000, 1 \leqslant n \leqslant 10^{8} 1A,B1000,1n108
  链接:link


2 思路

2.1 循环节

  命题 f n f_n fn序列是循环序列(从某个点开始),且循环节长度小于50。
  证明:设 a , b a,b a,b是不超过7的非负整数,则二元组 ( a , b ) (a,b) (a,b)一共有49种可能,所以 ( f i , f i + 1 ) , ( i ⩾ 1 ) (f_i ,f_{i+1}), (i \geqslant 1) (fi,fi+1),(i1)的前 50 50 50项一定存在重复元素。
  假设 ( f n , f n + 1 ) = ( f m , f m + 1 ) , ( 1 ⩽ n < m ⩽ 50 ) (f_n,f_{n+1})=(f_m,f_{m+1}),(1 \leqslant n < m \leqslant 50) (fn,fn+1)=(fm,fm+1),(1n<m50)。即 f n = f m , f n + 1 = f m + 1 f_n = f_m, f_{n+1} = f_{m+1} fn=fm,fn+1=fm+1,由递推公式 f n = ( A f n − 1 + B f n − 2 )   m o d   7 f_n = (Af_{n - 1} + Bf_{n - 2}) \ mod \ 7 fn=(Afn1+Bfn2) mod 7可得出 f n + 2 = f m + 2 f_{n+2}=f_{m+2} fn+2=fm+2,进而 f n + 3 = f m + 3 , f n + 4 = f m + 4 , . . . f_{n+3}=f_{m+3},f_{n+4}=f_{m+4},... fn+3=fm+3,fn+4=fm+4,...这样就可得知该序列从 n n n开始以 m − n m-n mn为循环长度循环。

  推论1:如果存在 f n = f m , f n + 1 = f m + 1 f_n=f_m,f_{n+1}=f_{m+1} fn=fm,fn+1=fm+1,则该序列从 n n n开始以 m − n m-n mn为循环长度循环。

  推论2:存在 1 ⩽ n ⩽ 49 1 \leqslant n \leqslant 49 1n49,使得 f n = f 50 , f n + 1 = f 51 f_n = f_{50},f_{n+1} = f_{51} fn=f50,fn+1=f51
  证明:已知一定存在 ( f n , f n + 1 ) = ( f m , f m + 1 ) , ( 1 ⩽ n < m ⩽ 50 ) (f_n,f_{n+1})=(f_m,f_{m+1}),(1 \leqslant n < m \leqslant 50) (fn,fn+1)=(fm,fm+1),(1n<m50),并且可以推出 f n + 2 = f m + 2 , f n + 3 = f m + 3 , f n + 4 = f m + 4 , . . . f_{n+2}=f_{m+2},f_{n+3}=f_{m+3},f_{n+4}=f_{m+4},... fn+2=fm+2,fn+3=fm+3,fn+4=fm+4,...那么当 m + k = 50 m+k=50 m+k=50时,可使得 f n + k = f 50 , f n + k + 1 = f 51 f_{n+k}=f_{50},f_{n+k+1}=f_{51} fn+k=f50,fn+k+1=f51,其中 1 ⩽ n + k < 50 1 \leqslant n+k < 50 1n+k<50

  根据推论1推论2,可在 [ 1 , 50 ] [1,50] [1,50]中找到一个长度小于 50 50 50的循环节。

2.1.1 时间复杂度分析

  1. 计算前 51 51 51项的时间复杂度为 O ( 51 ) \mathcal{O}(51) O(51)
  2. 1 ⩽ n ⩽ 49 1 \leqslant n \leqslant 49 1n49寻找 f ( n ) = f ( 50 ) , f ( n + 1 ) = f ( 51 ) f(n) = f(50),f(n+1) = f(51) f(n)=f(50),f(n+1)=f(51)的时间复杂度为 O ( 49 ) \mathcal{O}(49) O(49)
  3. 计算 f ( n ) f(n) f(n)的时间复杂度为 O ( 1 ) \mathcal{O}(1) O(1)

  总的时间复杂度为 O ( 1 ) \mathcal{O}(1) O(1)

2.1.2 代码

#include<iostream>
#include<cstdio>
using namespace std;
int a[52]={0,1,1},A,B,n;
int main(){
    while(scanf("%d %d %d",&A,&B,&n)&&A+B+n){
        int step,first;
        for(int i=3;i<=51;i++) a[i]=(A*a[i-1]+B*a[i-2])%7;
        for(int i=1;i<=48;i++){
            if(a[50]==a[i]&&a[51]==a[i+1]){
                first=i;
                break;
            }
        }
        step=50-first;
        if(n<50) printf("%d\n",a[n]);
        else printf("%d\n",a[(n-first)%step+first]);
    }
    return 0;
}

2.2 矩阵快速幂

  关于矩阵快速幂的详细介绍请参考:link
  这道题的矩阵快速幂的公式为: [ f n f n − 1 ] = [ A B 1 0 ] n − 2 [ f 2 f 1 ] \left[\begin{array}{c} f_n \\ f_{n-1} \end{array}\right]=\left[\begin{array}{cc} A & B \\ 1 & 0 \end{array}\right]^{n-2}\left[\begin{array}{c} f_2 \\ f_1 \end{array}\right] [fnfn1]=[A1B0]n2[f2f1]

2.2.1 时间复杂度分析

  矩阵快速幂的时间复杂度为 O ( m 3 log ⁡ n ) \mathcal{O}(m^{3}\log{n}) O(m3logn),其中 m m m为矩阵大小, n n n为幂指数。
  对于这道题来说,时间复杂度为 O ( log ⁡ n ) \mathcal{O}(\log{n}) O(logn)

2.2.2 代码

#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=15;
const int MOD=7;
int A,B,n,sz=2;
struct Matrix{
    int dt[MAXN][MAXN];
    Matrix(int tp=0){
        for(int i=1;i<=sz;i++){
            for(int j=1;j<=sz;j++){
                if(i==j) dt[i][j]=tp;
                else dt[i][j]=0;
            }
        }
    }
    Matrix operator+(const Matrix& a){
        Matrix res;
        for(int i=1;i<=sz;i++){
            for(int j=1;j<=sz;j++){
                res.dt[i][j]=(a.dt[i][j]+dt[i][j])%MOD;
            }
        }
        return res;
    }
    Matrix operator-(const Matrix& a){
        Matrix res;
        for(int i=1;i<=sz;i++){
            for(int j=1;j<=sz;j++){
                res.dt[i][j]=(dt[i][j]-a.dt[i][j])%MOD;
            }
        }
        return res;
    }
    Matrix operator*(const Matrix& a){
        Matrix res;
        for(int i=1;i<=sz;i++){
            for(int j=1;j<=sz;j++){
                for(int k=1;k<=sz;k++){
                    res.dt[i][j]=(res.dt[i][j]+dt[i][k]*a.dt[k][j])%MOD;
                }
            }
        }
        return res;
    }
};
Matrix qpow(const Matrix& a,int b){
    Matrix res(1),rem=a;
    while(b){
        if(b&1) res=res*rem;
        rem=rem*rem;
        b>>=1;
    }
    return res;
}
int main(){
    while(scanf("%d %d %d",&A,&B,&n)&&A+B+n){
        if(n<=2){
            printf("1\n");
            continue;
        }
        Matrix mat;
        mat.dt[1][1]=A;mat.dt[1][2]=B;
        mat.dt[2][1]=1;
        Matrix res=qpow(mat,n-2);
        printf("%d\n",(res.dt[1][1]+res.dt[1][2])%7);
    }
    return 0;
}

2.3 算法比较

  从最坏的时间复杂度来看,循环节更具有优势,因为循环节最多计算 51 + 49 + 1 = 101 51+49+1=101 51+49+1=101次,而矩阵快速幂要计算 2 3 × log ⁡ ( 1 0 8 ) = 212 2^{3} \times \log(10^{8}) = 212 23×log(108)=212次。
  总的来说循环节的时间复杂度只与模有关,而矩阵快速幂的时间复杂度只与n有关。所以当模较大时应当采用矩阵快速幂,当模较小且n较大时可采用循环节的做法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值