有一个巨大的n行m列的矩阵。这个矩阵满足一个神奇的性质:若用F[i][j]来表示矩阵中第i行第j列的元素,则F[i][j]满足下面的递推式:
F[1][1]=1
F[i,j]=a*F[i][j-1]+b (j!=1)
F[i,1]=c*F[i-1][m]+d (i!=1)
递推式中a,b,c,d都是给定的常数。
现在问你F[n][m]的值是多少。由于最终结果可能很大,你只需要输出F[n][m]除以1,000,000,007的余数。
这道题的数据范围是在我做过的题中是比较大的,n和m<=10^1000000,所以做法肯定不一般。首先我们讲一下50分的做法,就是矩阵乘法加快速幂。这道题构造难度中等,首先构造出pre1为{a,b}{0,1},pre2为{c,d}{0,1},为什么,看图片。接下来,定义ans一开始为per1^(m-1)(用快速幂求),让ans*{1}{1}(第一个1表示f[1][1]),那就可以得到{f[1][m]}{1},然后令ans=ans*pre2。因为ans * {f[1][m]}{1}={f[2][m]}{1}(pre2*{f[1][m]}{1}={f[2][1}{1}),所以只需让ans^(n-1)乘{f[1][m]}{1}={f[n][m]}{1},答案就出来了,50分的做法就是这样。
现在讲一下100分的做法,只需在50分的基础上加上牛掰的费马小定理就可以了。费马小定理简单可以理解为a^(p-1)≡1 (mod p),所以可以把n,m的超大范围简化成10^9。但是需要特判一下题目中a,c都为1的情况。这题在经过这样的过程后,便解决了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define mod 1000000007
using namespace std;
typedef long long LL;
struct node
{
LL a[5][5];
node()
{
memset(a,0,sizeof(a));
}
};
node pre1,pre2,f,ans;
node chengfa1(node a,node b)
{
node c;
for(int i=1;i<=2;i++)
{
for(int j=1;j<=2;j++)
{
for(int k=1;k<=2;k++)
{
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j])%mod;
}
}
}
return c;
}
node chengfa2(node a,node b)
{
node c;
for(int i=1;i<=2;i++)
{
for(int k=1;k<=2;k++)
{
c.a[i][1]=(c.a[i][1]+a.a[i][k]*b.a[k][1])%mod;
}
}
return c;
}
node power(int x,node pre)
{
node ans;
ans.a[1][1]=ans.a[2][2]=1LL;
while(x>0)
{
if(x%2==1)ans=chengfa1(pre,ans);
pre=chengfa1(pre,pre);
x/=2;
}
return ans;
}
char s1[1100000],s2[1100000];
int main()
{
LL n=0LL,m=0LL,a,b,c,d,x,p;
scanf("%s%s%lld%lld%lld%lld",s1+1,s2+1,&a,&b,&c,&d);
p=mod-1;
if(a==1 && c==1)p=mod;//一定不能漏
int len1=strlen(s1+1),len2=strlen(s2+1);
for(int i=1;i<=len1;i++)n=(n*10+s1[i]-'0')%p;
for(int i=1;i<=len2;i++)m=(m*10+s2[i]-'0')%p;
pre1.a[1][1]=a;pre1.a[1][2]=b;pre1.a[2][2]=1LL;
pre2.a[1][1]=c;pre2.a[1][2]=d;pre2.a[2][2]=1LL;
f.a[1][1]=f.a[2][1]=1LL;
ans=power(m-1,pre1);
f=chengfa2(ans,f);
ans=chengfa1(ans,pre2);
ans=power(n-1,ans);
f=chengfa2(ans,f);
printf("%lld\n",f.a[1][1]);
return 0;
}