Q老师的考验

Q老师 对数列有一种非同一般的热爱,尤其是优美的斐波那契数列。
这一天,Q老师 为了增强大家对于斐波那契数列的理解,决定在斐波那契的基础上创建一个新的数列 f(x) 来考一考大家。数列 f(x) 定义如下:
当 x < 10 时,f(x) = x;
当 x ≥ 10 时,f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10),ai 只能为 0 或 1。
Q老师 将给定 a0~a9,以及两个正整数 k m,询问 f(k) % m 的数值大小。
聪明的你能通过 Q老师 的考验吗?
Input
输出文件包含多组测试用例,每组测试用例格式如下:

第一行给定两个正整数 k m(k < 2e9, m < 1e5)

第二行给定十个整数,分别表示 a0~a9

Output
对于每一组测试用例输出一行,表示 f(k) % m 的数值大小。

Sample Input

10 9999
1 1 1 1 1 1 1 1 1 1
20 500
1 0 1 0 1 0 1 0 1 0

Sample Output

45
104

思路:

难点在于k取值范围太大,暴力从前往后推搞不定。为此我们可以采取一种很巧妙的方法——矩阵快速幂,这种方法经常可以用于降低线性递推的复杂度。
首先要构造矩阵的递推式,从而找到常数矩阵。矩阵递推式如下:
在这里插入图片描述
进而可以推导到如下:
在这里插入图片描述
在这里插入图片描述
最后答案就是F(n)的第一个数。
利用快速幂,可以很快计算出上式中矩阵的(n-9)次方(注意矩阵乘法取模),从而解决问题。快速幂计算方法与整数的快速幂基本相同。
代码有详细注释。

代码:

#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 12;
int a[maxn], k, M;

struct Matrix
{
    int m[maxn][maxn];

    void ini(int *a)
    {//初始化矩阵A
        memset(m, 0, sizeof(m));
        for (int i = 1; i < maxn - 1; i++)
        {

            m[1][i] = a[i];
            if (i < maxn - 2)
                m[i + 1][i] = 1;
        }
    }
    void ini1()
    {//初始化矩阵F(9)
        memset(m, 0, sizeof(m));
        for (int i = 1; i < maxn - 1; i++)
        {
            m[i][1] = 10 - i;
        }
    }
    Matrix() {}   //默认构造

    Matrix(const Matrix &t)
    {  //复制构造
        memcpy(m, t.m, sizeof(m));
    }

    Matrix operator*(const Matrix &t) const
    {  //乘法
        Matrix res;
        for (int i = 1; i < maxn - 1; i++)
        {
            for (int j = 1; j < maxn - 1; j++)
            {
                res.m[i][j] = 0;
                for (int k = 1; k < maxn - 1; k++)
                {
                    res.m[i][j] += (m[i][k] * t.m[k][j]) % M;
                }
                res.m[i][j] %= M;   //注意取模
            }
        }
        return res;
    }
};
Matrix quick_pow(Matrix a, Matrix b, int x)
{//快速幂
    while (x)
    {
        if (x & 1)
        {
            b = a * b;
        }  
        a = a * a;
        x >>= 1;
    }
    return b;
}

Matrix A, f;
int main()
{
    while (~scanf("%d %d", &k, &M))
    {
        for (int i = 1; i <= 10; i++)
            scanf("%d", &a[i]);
        A.ini(a);
        f.ini1();
        f = quick_pow(A, f, k-9);
        printf("%d\n", f.m[1][1]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值