【NOI2013T4】矩阵游戏-矩阵优化+十进制快速幂
测试地址:矩阵游戏
做法:观察递推式,很明显可以用矩阵优化,设矩阵C={a b \n 0 1},D={c d \n 0 1},所以F[1][m]=C^(m-1)*{1 \n 1},再推出F[2][1]=D*C^(m-1)*{1 \n 1},数学归纳得F[n][1]=(D*C^(m-1))^(n-1)*{1 \n 1},所以F[n][m]=C^(m-1)*(D*C^(m-1))^(n-1)*{1 \n 1},于是问题就转化为求矩阵快速幂,然而n和m达到10^1000000,所以计算n-1和m-1时要用到一点高精度运算(这个不会...你还来看这个干什么),而且要使用十进制快速幂,即可节省进制转换的时间,复杂度O(lgN)。其实还可以加一点常数优化,注意到计算时2*2矩阵的第二排都是“0 1”,所以我们可以直接省略这个信息,这样矩阵乘法就变成这样一个运算:{a b}*{c d}={a*c a*d+b},优化了常数。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mod 1000000007
using namespace std;
char sn[1000010],sm[1000010];
long long a,b,c,d;
struct hd {int s[1000010],len;} n,m;
struct mat {long long a,b;} A[1000010],C,ANS;
mat mult(mat A,mat B)
{
mat S;
S.a=(A.a*B.a)%mod;
S.b=((A.a*B.b)%mod+A.b)%mod;
return S;
}
mat power(hd s)
{
mat S;
S.a=1,S.b=0;
for(int i=s.len-1;i>=0;i--)
{
for(int j=1;j<=s.s[i];j++)
S=mult(S,A[s.len-i-1]);
}
return S;
}
int main()
{
scanf("%s%s%lld%lld%lld%lld",sn,sm,&a,&b,&c,&d);
n.len=strlen(sn),m.len=strlen(sm);
for(int i=0;i<n.len;i++) n.s[i]=sn[i]-'0';
for(int i=0;i<m.len;i++) m.s[i]=sm[i]-'0';
n.s[n.len-1]--,m.s[m.len-1]--;
for(int i=n.len-1;i>=0;i--)
{
if (n.s[i]<0)
{
n.s[i]=9;
n.s[i-1]--;
}
else break;
}
for(int i=m.len-1;i>=0;i--)
{
if (m.s[i]<0)
{
m.s[i]=9;
m.s[i-1]--;
}
else break;
}
A[0].a=a,A[0].b=b;
for(int i=1;i<=1000001;i++)
{
A[i]=A[i-1];
for(int j=1;j<=9;j++)
A[i]=mult(A[i],A[i-1]);
}
C=power(m);
A[0].a=c,A[0].b=d;
A[0]=mult(A[0],C);
for(int i=1;i<=1000001;i++)
{
A[i]=A[i-1];
for(int j=1;j<=9;j++)
A[i]=mult(A[i],A[i-1]);
}
ANS=mult(C,power(n));
printf("%lld",(ANS.a+ANS.b)%mod);
return 0;
}