NYOJ301-递推求值

                                                            递推求值

       nyoj上矩阵专题里的10道题水了AC率最高的5道,惭愧,还不是完全自己写的,用了几乎两周的时间。模板题我是有自信写出来的,但对于高级一点的矩阵构造,我还是菜的抠脚。

     这题感谢MQL大哥和她女票指点,自己想了一天不会构造矩阵,然后两位巨巨一起讨论了一下,瞬间明白了。此题关键就是在于这个矩阵构造。

     题意:给出M斐波那契的前两项f[1],f[2],以及递推式:f(x)=a*f(x-2)+b*f(x-1)+c 中的a,b,c,和n.求f[n]%1000007。

    很裸的矩阵快速幂,但怎么构造这个矩阵呢。摘用远航学长的博客来解释吧:

    分析:由于n的值比较大,所以常规方法肯定会超时。根据递推式求第n个表达式的值时,通常用矩阵乘法来做。

本题要构造两个矩阵,其中一个为矩阵A,作为初始矩阵

    即:假设初始矩阵为A,那么每次都需要乘以下一个矩阵B来得到下一项。

   而:f[n]=a*f[n-2]+b*f[n-1]+c,f[n-1]=1*f[n-1]+0*f[n-2]+0*c,c=0*f[n-1]+0*f[n-2]+1*c;

A
| f2  0   0  |
| f1  0   0  |
| 1   0   0  |
B
| b   a   c |
| 1   0   0 |
| 0   0   1 |

  因为F(2)和F(1)是已知的,当n>=3时,每次都乘以矩阵B,就能推出下一个矩阵。而矩阵的第一行第一列的元素就是所求的结果。

所以利用矩阵快速幂能够快速准确地求出结果。

    由以上就可以很快得出结果了:
const ll MOD=1e6+7;
const int N=1e3+10;
ll x1,x2,aa,bb,cc,n;
struct mat
{
    ll a[3][3];
};
mat mat_mul(mat x,mat y)
{
    mat res;
    memset(res.a,0,sizeof(res.a));
    for(int i=0; i<3; i++)
        for(int j=0; j<3; j++)
            for(int k=0; k<3; k++)
                res.a[i][j]=(res.a[i][j]+x.a[i][k]*y.a[k][j])%MOD;
    return res;
}
mat mat_fast_pow(mat x,ll k)
{
    mat res;
    memset(res.a,0,sizeof(res.a));
    for(int i=0; i<3; i++) res.a[i][i]=1;
    while(k)
    {
        if(k&1) res=mat_mul(res,x);
        x=mat_mul(x,x);
        k=k>>1;
    }
    return res;
}
void solve()
{
    mat B,A;//
    memset(B.a,0,sizeof(B.a));
    memset(A.a,0,sizeof(A.a));
    B.a[0][0]=bb,B.a[0][1]=aa,B.a[0][2]=cc;//构造B矩阵;
    B.a[1][0]=B.a[2][2]=1;
    A.a[0][0]=x2,A.a[1][0]=x1,A.a[2][0]=1;//构造初始矩阵A,注意顺序
    B=mat_fast_pow(B,n-2);//矩阵快速幂
    mat ans=mat_mul(B,A);//相乘
    printf("%lld\n",ans.a[0][0]);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld%lld%lld%lld%lld",&x1,&x2,&aa,&bb,&cc,&n);
        if(aa<0) aa+=MOD;//注意数据范围和题目要求;
        if(bb<0) bb+=MOD;
        if(cc<0) cc+=MOD;
        if(n==1)//特判1和2的情况
        {
            printf("%lld\n",x1);
            continue;
        }
        if(n==2)
        {
            printf("%lld\n",x2);
            continue;
        }
        solve();
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值