uva 10518 How Many Calls?

数学递推(考查矩阵二分快速幂取模)

参考:http://www.cnblogs.com/staginner/archive/2011/12/14/2288187.html

输入n和M,简单来说就是要求f(n)%M,而f(n)=2*F(n)-1,F(n)为第n项费波那列数,所以问题转化为求F(n),由于n非常大n (0 <= n < 263-1)

所以线性递推会超时,要用矩阵快速幂的方法

初始化[F0,F1]=[0,1] , 要求[Fn,Fn+1]


[F0,F1]  *  |0 1| (n)  =   [Fn,Fn+1] (不考虑高精度)
                 |1 1|

 

用二分的方法求解|0 1| (n)
                        |1 1|

 

//初始化[f0,f1],要求[fn,fn+1]

//[f0,f1] * |0 1| (n)  =   [fn,fn+1]    (不考虑高精度)

//          |1 1|

#include <cstdio>

#include <cstring>

#define MAX 550



long long b[MAX][2];

long long N,M;



void pow_mod(long long n ,int c)

{

    if(n==1)  //递归边界,矩阵为0,1,1,1

    {

        b[c][0]=0;   b[c][1]=1;

        b[c+1][0]=1; b[c+1][1]=1;

        return ;

    }

    pow_mod(n/2,c+2);

    //递归,每次递归都产生一个矩阵,一个矩阵占两行所以c+2



    long long A=b[c+2][0] , B=b[c+2][1] , C=b[c+3][0] , D=b[c+3][1];

    b[c][0]=  ( A*A+B*C )%M;

    b[c][1]=  ( A*B+B*D )%M;

    b[c+1][0]=( C*A+D*C )%M;

    b[c+1][1]=( C*B+D*D )%M;



    if(n&1) //奇数还要乘上矩阵{0,1,1,1}

    {

        long long tmp;

        tmp=b[c][0];   b[c][0]=b[c][1];     b[c][1]+=tmp;

        tmp=b[c+1][0]; b[c+1][0]=b[c+1][1]; b[c+1][1]+=tmp;

    }



    //打印当前矩阵

//    printf("\n| %lld %lld |\n",b[c][0],b[c][1]);

//    printf("| %lld %lld |\n",b[c+1][0],b[c+1][1]);



    return ;

}



void solve()

{

    memset(b,0,sizeof(b));

    //b用来保存递归过程中的矩阵,用空间换取时间

    pow_mod(N,0);

    //矩阵二分快速幂取模

    printf("%lld\n",(2*b[1][1]-1+M)%M);

    //可知F(n)就位于b[1][1]这个位置

}



int main()

{

    int T=0;

    while(scanf("%lld%lld",&N,&M)!=EOF)

    {

        if(!N && !M) break;

        printf("Case %d: %lld %lld ",++T,N,M);

        if(!N) printf("0\n");  //特判

        else   solve();

    }

    return 0;

}

 

你可能感兴趣的:(call)