您的位置:首页 > 其它

uva 10518 How Many Calls?

2013-02-07 14:02 295 查看
数学递推(考查矩阵二分快速幂取模)

参考:/article/4928200.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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: