版权声明:蒟蒻原创博客,大佬转载也需附上链接: https://blog.csdn.net/weixin_43810158/article/details/89232840
题目描述:
守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条行的,守望者warden拥有一个技能名叫“闪烁”,这个技能可以把她传送到后面的监狱内查看,她比较懒,一般不查看完所有的监狱,只是从入口进入,然后再从出口出来就算完成任务了。
头脑并不发达的warden最近在思考一个问题,她的闪烁技能是可以升级的,k级的闪烁技能最多可以向前移动k个监狱,一共有n个监狱要视察,她从入口进去,一路上有n个监狱,而且不会往回走,当然她并不用每个监狱都视察,但是她最后一定要到第n个监狱里去,因为监狱的出口在那里,但是她并不一定要到第1个监狱。
守望者warden现在想知道,她在拥有k级闪烁技能时视察n个监狱一共有多少种方案?
输入:
第一行是闪烁技能的等级k(1<=k<=10)
第二行是监狱的个数n(1<=n<=2^31-1)
输出:
由于方案个数会很多,所以输出它 mod 7777777后的结果就行了
输入样例:
2 4
输出样例:
5
提示:
样例解释:
把监狱编号1 2 3 4,闪烁技能为2级,
一共有5种方案
→1→2→3→4
→2→3→4
→2→4
→1→3→4
→1→2→4
思路分析:
这一题的DP我们可以轻易看出,设f[i]为到i节点的方案数,而状态转移方程就是:
但是众所周知,是一个较大的数,那么我们的矩阵加速就上场了。
设b为加速矩阵,假设k为4则:
我们再设一个矩阵ans[1][k]
其中ans[1][1]表示f[i]的值,而ans[1][2]为f[i-1],以此类推。
那我们的加速矩阵的作用就一目了然了。
第一列就是将f[i]的值求出,而之后的只不过是转移了。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define ll long long
ll n1,mod=7777777,k;
struct node{
ll n,m,c[15][15];
node()
{
memset(c,0,sizeof(c));
}
node operator*(const node &a)
{
node 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;
}
}b,ans;
void qkpow(node a,ll x)
{
while(x)
{
if(x&1)
ans=ans*a;
a=a*a;
x/=2;
}
}
int main()
{
scanf("%lld%lld",&k,&n1);
b.n=k;
b.m=k;
for(int i=1;i<=k;i++)
b.c[i][1]=1;
for(int i=1;i<=k;i++)
b.c[i][i+1]=1;
ans.n=1;
ans.m=k;
ans.c[1][1]=1;
qkpow(b,n1);
printf("%lld",ans.c[1][1]);
}