[矩阵加速] Warcraft III 守望者的烦恼


题目描述

Warcraft III 守望者的烦恼

解题思路

我们先看题目来列出这道题的递推式,因为一个点可以由它前 k k k个点中的任意一个点来得到,设守望者走到第 i i i号监狱需要 f ( i ) f(i) f(i)步,那么 f ( i ) = f ( i − 1 ) + f ( i − 2 ) + … + f ( i − k ) f(i)=f(i-1)+f(i-2)+…+f(i-k) f(i)=f(i1)+f(i2)++f(ik)。但是当 i &lt; k i&lt;k i<k时, f ( i ) f(i) f(i)就是一个前缀和了
如果细心一点观察,可以发现这个递推式和斐波那契数列有一点相像,因为数据范围太大,我们采用矩阵加速来解决

如果题目要求“闪烁”技能为 k k k级,那么我们就定义一个 A A A矩阵为 [ f ( 1 ) , f ( 2 ) … f ( k ) ] \begin{bmatrix}f(1), f(2)…f(k)\end{bmatrix} [f(1),f(2)f(k)],那么我们需要让 A A A矩阵乘以一个 B B B矩阵使它变成 [ f ( 2 ) , f ( 3 ) … f ( k + 1 ) ] \begin{bmatrix}f(2),f(3)…f(k+1)\end{bmatrix} [f(2),f(3)f(k+1)]

所以 B B B矩阵就因该为一个 k ∗ k k*k kk的矩阵,很容易想到 B B B矩阵的构造法:
1、最后一列全部为1
2、从第2行第2列开始,右下方的斜线上全部为1
B B B矩阵为: [ 0 , 0 , 0 , 0 … 0 , 1 1 , 0 , 0 , 0 … 0 , 1 0 , 1 , 0 , 0 … 0 , 1 … … 0 , 0 , 0 , 0 … 1 , 1 ] \begin{bmatrix}0,0,0,0…0,1\\ 1,0,0,0…0,1\\ 0,1,0,0…0,1\\ ……\\0,0,0,0…1,1\end{bmatrix} 0,0,0,00,11,0,0,00,10,1,0,00,10,0,0,01,1
然后再用一个矩阵快速幂求解就可以了

参考代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define reg register
 
template <class T>
inline T read() {
    T x = 0; T f = 1; char s = getchar();
    while(s < '0' || s > '9') {if(s == '-') f = -1; s = getchar();}
    while(s >= '0' && s <= '9') {x = (x << 3) + (x << 1) + s - 48; s = getchar();}
    return x * f;
}
 
template <typename T>
inline void wri(T x) {
    if(x < 0) {x = -x; putchar('-');}
    if(x / 10) wri(x / 10);
    putchar(x % 10 + 48);
}
 
template <typename T>
inline void write(T x, char s) {
    wri(x);
    putchar(s);
}
 
#define LL long long
 
const LL mod = 7777777;
 
LL k, n;
 
struct matrix {
    int n, m;
    LL a[15][15];
 
    inline void cl() {
        n = m = 0;
        memset(a, 0, sizeof a);
    }
 
    inline matrix operator * (const matrix &rhs) const {	//矩阵乘法
        matrix ret;
        ret.cl();
        ret.n = n, ret.m = rhs.m;
        for(reg int i = 1;i <= n;i ++)
            for(reg int j = 1;j <= rhs.m;j ++)
                for(reg int k = 1;k <= m;k ++)
                    ret.a[i][j] = (ret.a[i][j] + a[i][k] * rhs.a[k][j] % mod) % mod;
        return ret;
    }
}A, B, ans;
 
inline matrix qkpow(matrix x, LL y) {	//矩阵快速幂
    matrix ret;
    ret.cl();
    ret.n = x.n, ret.m = x.m;
    for(reg int i = 1;i <= ret.n;i ++)
        ret.a[i][i] = 1;
    while(y) {
        if(y & 1)
            ret = ret * x;
        x = x * x;
        y >>= 1;
    }
    return ret;
}
 
int main() {
    k = read<LL>(), n = read<LL>();
    A.n = 1, A.m = k, B.n = B.m = k;
    A.a[1][1] = 1;
    int tmp = 1;
    for(reg int i = 2;i <= k;i ++) {	//矩阵初始化
        A.a[1][i] = tmp;
        tmp += A.a[1][i];
    }
    for(reg int i = 1;i <= B.m;i ++) {
        if(i != B.m)
            B.a[i + 1][i] = 1;
        else
            for(reg int j = 1;j <= B.n;j ++)
                B.a[j][i] = 1;
    }
    ans = A * qkpow(B, n + 1 - k);	//快速幂求解
    write(ans.a[1][ans.m], '\n');
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值