C++矩阵加速经典题目:Warcraft III 守望者的烦恼 [vijos 1067]

Warcraft III 守望者的烦恼

背景
守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条行的,守望者warden拥有一个技能名叫“闪烁”,这个技能可以把她传送到后面的监狱内查看,她比较懒,一般不查看完所有的监狱,只是从入口进入,然后再从出口出来就算完成任务了。

描述
头脑并不发达的warden最近在思考一个问题,她的闪烁技能是可以升级的,k级的闪烁技能最多可以向前移动k个监狱,一共有n个监狱要视察,她从入口进去,一路上有n个监狱,而且不会往回走,当然她并不用每个监狱都视察,但是她最后一定要到第n个监狱里去,因为监狱的出口在那里,但是她并不一定要到第1个监狱。

守望者warden现在想知道,她在拥有k级闪烁技能时视察n个监狱一共有多少种方案?

格式
输入格式
第一行是闪烁技能的等级k(1<=k<=10)
第二行是监狱的个数n(1<=n<=2^31-1)

输出格式
由于方案个数会很多,所以输出它 mod 7777777后的结果就行了

样例1
样例输入1
2
4
样例输出1
5
限制
各个测试点1s

提示
把监狱编号1 2 3 4,闪烁技能为2级,
一共有5种方案
→1→2→3→4
→2→3→4
→2→4
→1→3→4
→1→2→4

小提示:建议用int64,否则可能会溢出

这道题我们可以先考虑k=2的情况,发现其实此题的主要内容就是爬楼梯(k为最多爬多少步,n为有n阶楼梯)。所以在k=2时整个解排出来便是一个裴波那契数列,递推式为\(f_{n}=f_{n-1}+f_{n-2}\)。那么扩展到闪烁步数最大为k的情况,不难发现递推式便为\(f_{n}=f_{n-1}+f_{n-2}+f_{n-3}+...+f_{n-k}\)

这里有一个初始化的问题让我思考了一段时间,这里阐述一下。如果k是小于n的,那么总的方案数会是多少。不难发现在\(k>n\)时,你最多一次只能爬n步,而不能爬k步,所以在\(k>n\)时,其实方案数就是\(f_{n}\),也就是\(f_{n}=f_{n-1}+f_{n-2}+...+f_{0}\)

现在来考虑矩阵加速,方法类似于裴波那契数列的构造方法,具体请看此篇博客:https://blog.csdn.net/weixin_44049566/article/details/88946702
这里因为矩阵的阶数随k而定,所以可以使用循环来构造,类似于如下代码:

void pre1(int k) {
        n=m=k;
        for(int i=1;i<=k-1;i++) c[i+1][i]=1;
        for(int i=1;i<=k;i++) c[i][k]=1;
    }    

加速矩阵构造出来类似于这样:
\(\begin{bmatrix} & 0 & 0 & ... &0 \\ & 1 & 0& ... & 0\\ & 0 & 1& ... & 0\\ & ... & ... &... & ...\\ & 0 & 0& ... &1 \end{bmatrix}\)*\(\begin{bmatrix} & f_{1}&f_{2}&...&f_{k}\end{bmatrix}\)

代码

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
   
#define N 20
#define LL long long
#define MOD 7777777
 
LL k,n;
 
struct Matrix {
    LL n,m,c[N][N];
    Matrix() { memset(c,0,sizeof(c)); };
    void _read() {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%lld",&c[i][j]);
    }
    Matrix operator * (const Matrix& a) {
        Matrix r;
        r.n=n;r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]= (r.c[i][j]+ (c[i][k] * a.c[k][j])%MOD)%MOD;
        return r;
    }
    void _print() {
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=m;j++) {
                if(j!=1) cout<<" ";
                cout<<c[i][j];
            }
            if(i!=n) puts("");
        }
    }
    void pre1(int k) {
        n=m=k;
        for(int i=1;i<=k-1;i++) c[i+1][i]=1;
        for(int i=1;i<=k;i++) c[i][k]=1;
    }    
    void pre2(int k) {
        n=1,m=k;
        c[1][0]=c[1][1]=1;
        for(int i=2;i<=k;i++)
            for(int j=i-1;j>=0;j--)
                c[1][i]+=c[1][j];
    }
    Matrix _power(int indexx) {
        Matrix tmp,sum;tmp.pre1(k);
        for(int i=1;i<=k;i++) sum.c[i][i]=1;sum.m=sum.n=k;
        while(indexx>0) {
            if(indexx&1) sum=sum*tmp;
            tmp=tmp*tmp;
            indexx/=2;
        }
        return sum;
    }
     
}A,B;
 
int main() {
    cin>>k>>n;
    A.pre2(k);
    B.pre1(k);
    B=B._power(n-k);
    A=A*B;
    cout<<A.c[1][k];
}

猜你喜欢

转载自www.cnblogs.com/MisakaMKT/p/10689051.html