[bzoj 3240--Noi2013]矩阵游戏

124 篇文章 2 订阅
19 篇文章 0 订阅

有一个巨大的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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值