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;
}

 

posted @ 2013-02-07 14:02  Titanium  阅读(370)  评论(0编辑  收藏  举报