HDU 1588 比较综合的一个矩阵快速幂

这个题目感觉是比较综合的一个题目了,这个用了好几个公式,很值得记住啊。

一开始在之前遇到了一个斐波那契数列的问题如下:

•eg.求斐波那契数列第n项(n<=10^18)
•f[0]=0,f[1]=1,f[i]=f[i-1]+f[i-2](i>=2)
这个题目的求解的话,就是建立一个矩阵的递推式,然后矩阵快速幂就可以轻松解出来的。

这个就是一般的对斐波那契数列的推导,其中那个2*2的矩阵暂记A。
然后还有一个对斐波那契数列的推导方法,如下所示:

然后这个公式就可以拿来解决这个题目了,这个题目求的是对g函数代入就相当于是
f[b]+f[k+b]+f[2*k+b]+...+f[k*(n-1)+b]
然后这个式子的求解就可以看成是A^b+A^(k+b)+A^(2*k+b)+...+A^(k*(n-1)+b)这个求出来的就相当于是上面的那个2*2的矩阵的结果了,然后要取得就是这个矩阵的第一行第二列的f的值即可。这样就求出来了累加的 f[b]+f[k+b]+f[2*k+b]+...+f[k*(n-1)+b]值了。
下面的就是求解 A^b+A^(k+b)+A^(2*k+b)+...+A^(k*(n-1)+b)的问题了。
求解这个可以先提取了A^b这个矩阵后形成了
A^b * ( A^0 + A^k + A^(2*k) + ... + A^((n-1)*k) )
再可以化简成
A^b * ( (A^k)^0 + (A^k)^1 + (A^k)^2 + ...+ (A^k)^(n-1) )
然后把A^k看成是一个矩阵P即可,就成了
求A^b和求(E+P+P^2+...+P^(n-1)),前面直接矩阵快速幂,然后后面的这个就是前一篇博客写到的这个求解的问题了。见前面的博客:http://blog.csdn.net/qq_23702679/article/details/52770942 点击打开链接
最后这个问题就解决了,感觉真的是蛮综合的问题的啊,解决起来根本是想不到,只是在第一个斐波那契序列的递推式那里在琢磨,根本没有想到这个第二的这种递推式子,看起来对于矩阵的递推式子的积累还是很重要的啊。

贴上代码:
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;

int k,b,n,m;
struct Mat
{
    long long int sm[3][3];
}A,E,zero;

struct superMat
{
    Mat sm[3][3];
}MA,ME;

void init()
{
    A.sm[0][0]=A.sm[0][1]=A.sm[1][0]=1;A.sm[1][1]=0;
    E.sm[0][0]=E.sm[1][1]=1;E.sm[0][1]=E.sm[1][0]=0;
    zero.sm[0][0]=zero.sm[0][1]=zero.sm[1][0]=zero.sm[1][1]=0;
    ME.sm[0][0]=ME.sm[1][1]=E;
    ME.sm[0][1]=ME.sm[1][0]=zero;
}

Mat mul(Mat a,Mat b)
{
    Mat tmp;
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            tmp.sm[i][j]=0;
            for(int k=0;k<2;k++)
            {
                tmp.sm[i][j]=(tmp.sm[i][j]+(a.sm[i][k]*b.sm[k][j])%m)%m;
            }
        }
    }
    return tmp;
}

Mat getMat(Mat a,int k)
{
    Mat tmp;
    Mat x,y,z;
    x=A;
    y=E;
 for(;k;k>>=1)
 {
     if(k&1)
        y=mul(x,y);
     x=mul(x,x);
 }
 return y;
}

Mat add(Mat a,Mat b)
{
    Mat tmp;
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            tmp.sm[i][j]=(a.sm[i][j]+b.sm[i][j])%m;
        }
    }
    return tmp;
}

superMat supermul(superMat x,superMat y)
{
    superMat tmp;
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            tmp.sm[i][j]=zero;
            for(int k=0;k<2;k++)
            {
                tmp.sm[i][j]=add(tmp.sm[i][j],mul(x.sm[i][k],y.sm[k][j]));
            }
        }
    }
    return tmp;
}

superMat getsuperMat(int n)
{
  superMat x,y,z;
  x=MA;
  y=ME;
  for(;n;n>>=1)
  {
      if(n&1)
        y=supermul(x,y);
      x=supermul(x,x);
  }
   return y;
}

int main()
{
   while(scanf("%d%d%d%d",&k,&b,&n,&m)!=EOF)
   {
     init();
     Mat Fb=getMat(A,b);
     Mat K=getMat(A,k);
     MA.sm[0][0]=K;
     MA.sm[0][1]=MA.sm[1][1]=E;
     MA.sm[1][0]=zero;
     superMat Q=getsuperMat(n);
     Mat z=Q.sm[0][1];
     Mat r=mul(z,Fb);
    printf("%lld\n",r.sm[0][1]);
   }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值