nyoj 301 递推求值【常数矩阵】

描述
给你一个递推公式:

f(x)=a*f(x-2)+b*f(x-1)+c

并给你f(1),f(2)的值,请求出f(n)的值,由于f(n)的值可能过大,求出f(n)对1000007取模后的值。

注意:-1对3取模后等于2

输入
第一行是一个整数T,表示测试数据的组数(T<=10000)
随后每行有六个整数,分别表示f(1),f(2),a,b,c,n的值。
其中0<=f(1),f(2)<100,-100<=a,b,c<=100,1<=n<=100000000 (10^9)
输出
输出f(n)对1000007取模后的值
样例输入
2
1 1 1 1 0 5
1 1 -1 -10 -100 3
样例输出
5
999896
来源
经典题目
思路:(详见白书201页)
这里写图片描述
之所以能够用快速幂计算矩阵,因为乘以矩阵后和乘以矩阵前变化的只有F的下标,常数部分不发生改变。
递推式:
这里写图片描述

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
typedef long long LL;
const LL mod = 1000007;
using namespace std;

LL t, f1, f2, a, b, c, n;

struct mat {
    LL mapp[3][3];
}base;

mat mat_mul(mat A, mat B) {
    mat C;
    memset(C.mapp, 0, sizeof(C.mapp));
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 3; j++) {
            for(int k = 0; k < 3; k++) {
                C.mapp[i][k] = (C.mapp[i][k] + A.mapp[i][j] * B.mapp[j][k] + mod) % mod;
            }
        }
    }
    return C;
}

mat mat_pow(mat A, LL b) {
    mat ans;
    memset(ans.mapp, 0, sizeof(ans.mapp));
    for(int i = 0; i < 3; i++) {
        ans.mapp[i][i] = 1;
    }
    while(b) {
        if(b & 1) ans = mat_mul(ans, A);
        A = mat_mul(A, A);
        b >>= 1;
    }
    return ans;
}

int main() {
    scanf("%lld", &t);
    while(t--) {
        scanf("%lld %lld %lld %lld %lld %lld", &f1, &f2, &a, &b, &c, &n);
        if(n == 1) {
            printf("%lld\n", (f1 + mod) % mod);
            continue;
        }
        if(n == 2) {
            printf("%lld\n", (f2 + mod) % mod);
            continue;
        }
        base.mapp[0][0] = b;
        base.mapp[0][1] = a;
        base.mapp[0][2] = c;
        base.mapp[1][0] = 1;
        base.mapp[1][1] = 0;
        base.mapp[1][2] = 0;
        base.mapp[2][0] = 0;
        base.mapp[2][1] = 0;
        base.mapp[2][2] = 1;
        mat A = mat_pow(base, n - 2);
        mat B, C;
        memset(B.mapp, 0, sizeof(B.mapp));
        B.mapp[0][0] = f2;
        B.mapp[1][0] = f1;
        B.mapp[2][0] = 1;
        memset(C.mapp, 0, sizeof(C.mapp));
        for(int i = 0; i < 3; i++) {
            for(int j = 0; j < 3; j++) {
                for(int k = 0; k < 3; k++) {
                    C.mapp[i][k] = (C.mapp[i][k] + A.mapp[i][j] * B.mapp[j][k] + mod) % mod;
                }
            }
        }
        printf("%lld\n", (C.mapp[0][0] + mod) % mod);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值