HDU4686---Arc of Dream(矩阵快速幂,大数据的坑题)

【题目来源】https://vjudge.net/problem/HDU-4686
【题意】
题意如题面所述。AD(i)=AD(i-1)+ai*bi。
【思路】
很明显,矩阵快速幂,但是这道题相当坑。
第一:若是没有n==0的情况会给出超时的结果。(我还一直在想log
的复杂度怎么会超时)
第二:乘法中间的过程必须各种取余,否则会爆longlong。
好了,步入正题:
很多博客直接给出了现成的矩阵式子或者图形,新手估计看的一脸懵逼,那么我就给出一下我的推导过程吧。
首先呢,找出总的关系式:
AD(i)=AD(i-1)+a(i)*b(i)。
然后把右边所有i换成i-1,因为矩阵是这么个意思:前一项乘以系数矩阵得到下一项,也就是第i-1项乘以系数矩阵得到第i项,所以两边的元素要分清。
AD(i)=AD(i-1)+(a(i-1)*ax+ay)(b(i-1)*bx+by)
化简得:
AD(i)=AD(i-1)+ax*bx*a(i-1)*b(i-1)+ax*by*a(i-1)+ay*bx*b(i-1)+ay*by;
这个时候呢,递推式就出来了,那么接下来写出系数矩阵:
(的第一行,,嘿嘿)由等号右边的式子可得系数:
1 ax*bx ax*by ay*bx ayby
上面呢系数矩阵的第一行,那么该怎么推以下几行呢?(继续看)
刚才已经说过,是第i-1项乘以系数矩阵得到第i项,那么每个式子都会的到下一项:(以下是初始矩阵(也就是右边除了系数之外的))
初始矩阵(坐标)当前项————>下一项(矩阵坐标)
AD(i-1)(1,1)—————–>AD(i)(1,1)
a[i-1]*b[i-1](2,1)————>a[i]*b[i](2,1)
a[i-1](3,1)——————->a[i](3,1)
b[i-1](4,1)——————->b[i](4,1)
1(5,1)———————–>1(5,1)
由于矩阵乘法可知:
AD(i)的坐标是(1,1),那么就等于系数矩阵的第一行乘以初始矩阵的第一列;
a[i]*b[i]的坐标是(2,1),那么就等于系数矩阵的第二行乘以初始矩阵的第二列;(以此来推出系数矩阵的第二行)
。。。
。。。
。。。
( 这也是一般系数矩阵的推理方法)
然后推出的系数矩阵是这样的:
这里写图片描述
然后这道题就可以做了。。。
【代码】

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const LL mod=1e9+7;
LL a0,ax,ay;
LL b0,bx,by;
struct mat
{
    LL a[6][6];
};
mat operator*(mat &s,mat &t)
{
    mat r;
    memset(r.a,0,sizeof(r.a));
    for(int i=1; i<=5; i++)
    {
        for(int j=1; j<=5; j++)
        {
            for(int k=1; k<=5; k++)
            {
                r.a[i][j]+=(s.a[i][k]*t.a[k][j])%mod;
                r.a[i][j]%=mod;
            }
        }
    }
    return r;
}
LL pow_mat(mat base,LL k)
{
    mat ans;
    for(int i=1;i<=5;i++)
        for(int j=1;j<=5;j++)
            ans.a[i][j]=(i==j);
    while(k)
    {
        if(k&1)
        {
            ans=ans*base;
        }
        base=base*base;
        k>>=1;
    }
    return (ans.a[1][1]*a0%mod*b0%mod+ans.a[1][2]*a0%mod*b0%mod+ans.a[1][3]*a0%mod+ans.a[1][4]*b0%mod+ans.a[1][5]%mod)%mod;//相当于初始矩阵与系数矩阵的乘法
}
int main()
{
    LL n;
    while(~scanf("%lld",&n))
    {
        scanf("%lld%lld%lld",&a0,&ax,&ay);
        scanf("%lld%lld%lld",&b0,&bx,&by);
        mat base;
        memset(base.a,0,sizeof(base.a));
        base.a[1][1]=1;
        base.a[1][2]=ax*bx%mod;
        base.a[1][3]=ax*by%mod;
        base.a[1][4]=ay*bx%mod;
        base.a[1][5]=ay*by%mod;
        base.a[2][2]=ax*bx%mod;
        base.a[2][3]=ax*by%mod;
        base.a[2][4]=ay*bx%mod;
        base.a[2][5]=ay*by%mod;
        base.a[3][3]=ax;
        base.a[3][5]=ay;
        base.a[4][4]=bx;
        base.a[4][5]=by;
        base.a[5][5]=1;
        if(n==0)
        {
            printf("0\n");
            continue;
        }
        printf("%lld\n",pow_mat(base,n-1));
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值