bzoj3286: Fibonacci矩阵

2 篇文章 0 订阅

题面在这里

题意:

题面写得很清楚。

做法:

首先想到矩阵快速幂。
发现n和m很大,log2承受不了。我们可以转化成10进制来做,原理和二进制一样,
先处理好乘0~9次的矩阵,每次p除以10,ans变成ans的十次方乘上原矩阵的v[i]次,v[i]是第i位的数字。
然后这题还要使用一些高级的卡常技巧,比如手打矩阵乘法之类。

顺便,这题1A感觉胖胖QWQ

代码:

/*************************************************************
    Problem: bzoj 3286 Fibonacci矩阵
    User: fengyuan
    Language: C++
    Result: Accepted
    Time: 10696 ms
    Memory: 12952 kb
    Submit_Time: 2017-12-27 14:30:20
*************************************************************/

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<cctype>
using namespace std;
typedef long long LL;

const int LEN = 1000010;
const int MOD = 2012182013;
struct Bignum{
    int v[LEN], len;
    inline void read() {
        char ch = getchar();
        for(; !isdigit(ch); ch = getchar());
        len = 0;
        for(; isdigit(ch); ch = getchar()) v[++ len] = ch-'0';
        for(int i = 1; i <= len/2; i ++) swap(v[i], v[len-i+1]);//个位在前,数位从低到高
    }
    inline void dec() {//减一
        v[1] --;
        for(int i = 1; i <= len; i ++)
            if(v[i] < 0) v[i] += 10, v[i+1] --;
            else break;
        while(len && v[len] == 0) len --;
    }
}n, m;
struct Matrix{
    //矩阵本来是3*3的,考虑到最后一列基本没用,于是压成2列
    LL v11, v12, v21, v22, v31, v32;
    inline Matrix() {}
    inline Matrix(int x) { (*this).set(x); }
    inline void set(int x) {
        v11 = v12 = v21 = v22 = v31 = v32 = 0;
        if(x) v11 = v22 = 1;//单位矩阵
    }
    inline void build(int a, int b, int c) {
        v11 = 0, v21 = 1, v31 = 0;
        v12 = a, v22 = b, v32 = c;
    }
    Matrix operator * (Matrix &x) {
        //优化常数,手打矩阵乘法
        Matrix ans;
        ans.v11 = (v11*x.v11+v12*x.v21)%MOD;
        ans.v12 = (v11*x.v12+v12*x.v22)%MOD;
        ans.v21 = (v21*x.v11+v22*x.v21)%MOD;
        ans.v22 = (v21*x.v12+v22*x.v22)%MOD;
        ans.v31 = (v31*x.v11+v32*x.v21+x.v31)%MOD;
        ans.v32 = (v31*x.v12+v32*x.v22+x.v32)%MOD;
        return ans;
    }
}ret, ans, M1, M2;

LL a, b, c, d, e, f;

inline LL get()
{
    char ch = getchar(); LL x = 0;
    for(; !isdigit(ch); ch = getchar());
    for(; isdigit(ch); ch = getchar()) x = (x*10+ch-'0')%MOD;
    return x;
}

//每一位单独用二进制计算
inline Matrix ksm2(Matrix x)
{
    int p = 10; Matrix ret(1);
    while(p) {
        if(p&1) ret = ret*x;
        x = x*x; p >>= 1;
    } return ret;
}

//十进制版矩阵快速幂,原理和二进制的差不多
inline Matrix ksm10(Matrix x, Bignum p)
{
    Matrix ans(1);
    Matrix fac[10]; fac[0].set(1);
    for(int i = 1; i <= 9; i ++) fac[i] = fac[i-1]*x;//先处理好乘0~9次的矩阵
    for(int i = p.len; i >= 1; i --)
        ans = ksm2(ans)*fac[p.v[i]];
    return ans;
}

int main()
{
    n.read(); m.read();
    a = get(); b = get(); c = get(); d = get(); e = get(); f = get();
    M1.build(a, b, c); M2.build(d, e, f);
    m.dec(); m.dec(); n.dec();
    ret = ksm10(M1, m);//先计算到第一行最后一个数
    ans = ksm10(ret*M2*M2, n)*ret;//计算到最后一行第一个数,再计算到最后一行最后一个数
    printf("%lld\n", (ans.v12+ans.v22+ans.v32)%MOD);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值