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

解題思路

首先看一下這個函數的線性遞推式:
f(x)={x,if(x<10)a0×f(x1)+a1×f(x2)+a2×f(x3)++a9×f(x10),if(x10) f(x) = \begin{cases} x,& if(x<10)\\ a0 \times f(x-1) + a1 \times f(x-2) + a2 \times f(x-3) + …… + a9 \times f(x-10),& if(x\ge 10) \end{cases}
注意k的取值範圍是k < 2e9,如果我們單純用線性遞推式來做,無法再1s中得到結果,聯想到矩陣快速冪可以用來求解線性遞推公式,因此我們使用矩陣快速冪來做。

構造的轉移矩陣如下所示:
[f(x)f(x1)f(x2)f(x3)f(x9)]1×10=[a0a1a2a3a910000010000010000000]10×10[f(x1)f(x2)f(x3)f(x4)f(x10)]1×10 \begin{bmatrix} f(x)\\ f(x-1) \\ f(x-2)\\ f(x-3)\\ \vdots\\ f(x-9) \end{bmatrix}_{1\times 10} =\begin{bmatrix} a_0 & a_1& a_2&a_3&\cdots& a_9\\ 1 & 0 & 0&0&\cdots&0\\ 0&1&0&0&\cdots&0\\ 0&0&1&0&\cdots&0\\ \vdots& \vdots & \vdots&\vdots&\ddots&\vdots \\ 0&0&0&0&\cdots&0\\ \end{bmatrix}_{10\times 10} \begin{bmatrix} f(x-1)\\ f(x-2) \\ f(x-3)\\ f(x-4)\\ \vdots\\ f(x-10) \end{bmatrix}_{1 \times 10}
根據這個轉移矩陣結合矩陣快速冪,可以將時間複雜度降低到O(logk)O(logk)

完整代碼

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

const int maxn=11;
struct Matrix{
    Matrix(int _size=0) {
        Size=_size; mat=new int*[Size];
        for (int i=0; i<Size; i++) mat[i]=new int[Size];
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                mat[i][j]=0;
    }
    Matrix(const Matrix& t){
        Size=t.Size; mat=new int*[Size];
        for (int i=0; i<Size; i++) mat[i]=new int[Size];
        memcpy(mat,t.mat,sizeof(mat));
    }
    ~Matrix(){
        for (int i=0; i<Size; i++) delete[] mat[i];
        delete[] mat;
    }
    Matrix operator*(const Matrix& t) const{
        Matrix ret(Size);
        for (int i=0; i<Size; ++i)
            for (int j=0; j<Size; ++j)
                for (int k=0; k<Size; ++k)
                    ret.mat[i][j]+=mat[i][k]*t.mat[k][j];
        return ret;
    }
    Matrix operator%(const int m) const {
        Matrix ret(Size);
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                ret.mat[i][j]=mat[i][j]%m;
        return ret;
    }
    Matrix& operator=(const Matrix& t){
        for (int i=0; i<Size; i++) delete[] mat[i];
        delete[] mat;
        Size=t.Size;
        mat=new int*[Size];
        for (int i=0; i<Size; i++) mat[i]=new int[Size];
        for (int i=0; i<Size; i++)
            for (int j=0; j<Size; j++)
                mat[i][j]=t.mat[i][j];
        return *this;
    }
    void quick_pow(int x,int m){//x次冪模m
        Matrix ret(Size);
        for (int i=0; i<Size; i++) ret.mat[i][i]=1;
        while(x){
            if(x&1) {
                ret=ret*(*this); ret=ret%m;
            }
            *this=(*this)*(*this);
            *this=(*this)%m;
            x>>=1;
        }
        *this=ret;
    }
    int Size;
    int** mat;
};
int a[maxn],k,m;
int getint(){
    int x=0,s=1; char ch=' ';
    while(ch<'0' || ch>'9'){ ch=getchar(); if(ch=='-') s=-1;}
    while(ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar();}
    return x*s;
}
int main(){
    //ios::sync_with_stdio(false);
    //cin.tie(0);
    while(scanf("%d %d",&k,&m)!=EOF){
        if(k<10){
            printf("k\n"); continue;
        }
        Matrix mat1(10);
        for (int i=0; i<10; i++){
            scanf("%d",&a[i]);
            mat1.mat[0][i]=a[i];
        }
        for (int i=1; i<10; i++)
            mat1.mat[i][i-1]=1;

        mat1.quick_pow(k-9,m);

        long long ans=0;
        for (int i=0; i<10; i++)
            ans+=mat1.mat[0][i]*(9-i);
        ans%=m;
        printf("%lld\n",ans);
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章