题目链接:https://www.luogu.org/problemnew/show/P1939
时空限制 1000ms / 128MB

题目描述

a[1]=a[2]=a[3]=1;
a[x]=a[x-3]+a[x-1](x>3).
求a数列的第n项对1000000007(10^9+7)取余的值。

输入格式

第一行一个整数T,表示询问个数。
以下T行,每行一个正整数n。

输出格式

每行输出一个非负整数表示答案。

输入样例

3
6
8
10

输出样例

4
9
19

说明

对于30%的数据 n<=100;
对于60%的数据 n<=2*10^7;
对于100%的数据 T<=100,n<=2*10^9.

解题思路

矩阵快速幂的模板。这道题目的关键之处在于构造初始矩阵,题目都告诉我们了要用矩阵加速,所以矩阵快速幂是核心所在。
我们首先要确定目标矩阵。下面这个矩阵就是我想要的矩阵.[ A[i], A[i−1], A[i−2] ]​
那么这个矩阵要怎样算出来。显然可以由上一项求得:

A[i-1]      A[i]
A[i-2] ---> A[i-1]
A[i-3]      A[i-2]

根据题目给出的递推式可以得到下面三个式子:

A[i] = A[i-1]*1 + A[i-2]*0 + A[i-3]*1
A[i-1] = A[i-1]*1 + A[i-2]*0 + A[i-3]*0
A[i-2] = A[i-1]*0 + A[i-2]*1 + A[i-3]*0

通过每一项的系数可以得出初始矩阵为

1 1 0
0 0 1
1 0 0

然后我们就可以通过矩阵快速幂进行求解。
值得注意的是,应该从第4项开始计算,即这个矩阵的N次方算出来的第一个元素是A[N+3],这样的话我们可以直接算N-3次就行了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
struct Mat {
    ll m[5][5];
}ans, a;
Mat Mul(Mat a, Mat b, int n) {
    Mat c;
    memset(c.m, 0, sizeof(c.m));
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= 3; j++)
            for (int k = 1; k <= 3; k++)
                c.m[i][j] = (c.m[i][j] + (a.m[i][k] * b.m[k][j]) % mod) % mod;
    return c;
}
int main() {
    int t, n;
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        if (n <= 3) {
            printf("1\n");
            continue;
        }
        n -= 3;
        memset(ans.m, 0, sizeof(ans.m));
        memset(a.m, 0, sizeof(a.m));
        ans.m[1][1] = ans.m[1][2] = ans.m[1][3] = 1;
        a.m[1][1] = a.m[1][2] = a.m[2][3] = a.m[3][1] = 1;
        while (n) {
            if (n & 1)
                ans = Mul(ans, a, 1);
            a = Mul(a, a, 3);
            n >>= 1;
        }
        printf("%lld\n", ans.m[1][1]);
    }
    return 0;
}