Codevs 1574 广义斐波那契数列(矩阵乘法)

1574 广义斐波那契数列
时间限制: 1 s
空间限制: 256000 KB
题目等级 : 钻石 Diamond
题目描述 Description
广义的斐波那契数列是指形如an=p*an-1+q*an-2的数列。今给定数列的两系数p和q,以及数列的最前两项a1和a2,另给出两个整数n和m,试求数列的第n项an除以m的余数。
输入描述 Input Description
输入包含一行6个整数。依次是p,q,a1,a2,n,m,其中在p,q,a1,a2整数范围内,n和m在长整数范围内。
输出描述 Output Description
输出包含一行一个整数,即an除以m的余数。
样例输入 Sample Input
1 1 1 1 10 7
样例输出 Sample Output
6
数据范围及提示 Data Size & Hint
数列第10项是55,除以7的余数为6。
分类标签 Tags
矩阵乘法 数论

/*
矩阵乘法快速幂.
矩阵还是比较好推的.....
要时刻想清楚最后的答案记在哪儿.
然后W了好几次.
ans先赋值乘一次.n-1.
把答案放在后边的话A1到An显然乘了n-2次....
*/
#include<iostream>
#include<cstdio>
#define MAXN 3
#define LL long long
using namespace std;
LL p,q,a1,a2,n,m;
LL a[MAXN][MAXN],ans[MAXN][MAXN],c[MAXN][MAXN],b[MAXN][MAXN];
void mi(int n)
{
    while(n)
    {
        if(n&1)
        {
            for(int i=1;i<=2;i++)
              for(int j=1;j<=2;j++)
                for(int k=1;k<=2;k++)
                c[i][j]=(c[i][j]+ans[i][k]*b[k][j]%m)%m;
            for(int i=1;i<=2;i++)
              for(int j=1;j<=2;j++)
                ans[i][j]=c[i][j],c[i][j]=0;
        }
        for(int i=1;i<=2;i++)
          for(int j=1;j<=2;j++)
            for(int k=1;k<=2;k++)
              c[i][j]=(c[i][j]+b[i][k]*b[k][j]%m)%m;
        for(int i=1;i<=2;i++)
          for(int j=1;j<=2;j++)
            b[i][j]=c[i][j],c[i][j]=0;
        n>>=1;
    }
}
void slove()
{
    a[1][1]=a1,a[1][2]=a2;
    b[1][2]=ans[1][2]=q,b[2][1]=ans[2][1]=1,
    b[2][2]=ans[2][2]=p;
    mi(n);
    printf("%lld",(a[1][1]*ans[1][2]%m+a[1][2]*ans[2][2]%m)%m);
}
int main()
{
    scanf("%d%d%d%d",&p,&q,&a1,&a2);
    cin>>n;cin>>m;
    n-=3;
    slove();
    return 0;
}
/*
结果在前边.
多乘一次.
*/
#include<iostream>
#include<cstdio>
#define MAXN 3
#define LL long long
using namespace std;
LL p,q,a1,a2,n,m;
LL a[MAXN][MAXN],ans[MAXN][MAXN],c[MAXN][MAXN],b[MAXN][MAXN];
void mi(int n)
{
    while(n)
    {
        if(n&1)
        {
            for(int i=1;i<=2;i++)
              for(int j=1;j<=2;j++)
                for(int k=1;k<=2;k++)
                c[i][j]=(c[i][j]+ans[i][k]*b[k][j]%m)%m;
            for(int i=1;i<=2;i++)
              for(int j=1;j<=2;j++)
                ans[i][j]=c[i][j],c[i][j]=0;
        }
        for(int i=1;i<=2;i++)
          for(int j=1;j<=2;j++)
            for(int k=1;k<=2;k++)
              c[i][j]=(c[i][j]+b[i][k]*b[k][j]%m)%m;
        for(int i=1;i<=2;i++)
          for(int j=1;j<=2;j++)
            b[i][j]=c[i][j],c[i][j]=0;
        n>>=1;
    }
    /*for(int i=1;i<=2;i++)
      for(int j=1;j<=2;j++)
        for(int k=1;k<=2;k++)
          c[i][j]=(c[i][j]+a[i][k]*ans[k][j]%m)%m;*/
}
void slove()
{
    a[1][1]=a1,a[1][2]=a2;
    b[1][2]=ans[1][2]=q,b[2][1]=ans[2][1]=1,
    b[2][2]=ans[2][2]=p;
    mi(n);
    printf("%lld",(a[1][1]*ans[1][1]%m+a[1][2]*ans[2][1]%m)%m);
}
int main()
{
    scanf("%d%d%d%d",&p,&q,&a1,&a2);
    cin>>n;cin>>m;
    n-=2;
    slove();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值