背景
守望者-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,否则可能会溢出
来源
杜杜我爱你个人原创
很容易想到递推式为f[i]=f[i-1]+f[i-2]+......+f[i-k]
然而由于n太大需要用矩乘优化
其对应矩阵的构造方法为:
在右上角的(n-1)*(n-1)的小矩阵中的主对角线上填1,矩阵第n行填对应的系数,其它地方都填0。
例如,我们可以用下面的矩阵乘法来计算f(n) = 4f(n-1) - 3f(n-2) + 2f(n-4)的第k项:
(2a-3c+4d即为f[n])
(该构造方法摘自传送门1)
这题就解决了
#include<cstdio>
#include<cstring>
const int mod=7777777,N=13;
int len;
inline void calc(int a[N][N],int b[N][N])
{
int c[N][N];
for(int i=1;i<=len;i++)
for(int j=1;j<=len;j++)
{
c[i][j]=0;
for(int k=1;k<=len;k++)
c[i][j]=( c[i][j]+ (long long)a[i][k]*b[k][j]%mod )%mod;
}
for(int i=1;i<=len;i++)
for(int j=1;j<=len;j++)
a[i][j]=c[i][j];
}
int f[N];
int fast(int n)
{
//自乘
int ans[N][N],a[N][N],b[N];
memset(ans,0,sizeof(ans));
memset(a,0,sizeof(a));
for(int i=1;i<=len;i++) ans[i][i]=1;
for(int i=2;i<=len;i++) a[i-1][i]=1;
for(int i=1;i<=len;i++) a[len][i]=1;
n-=10;
for(int i=len;i>=1;i--) b[len-i+1]=f[11-i];
while(n)
{
if(n&1) calc(ans,a);
n=n>>1;
calc(a,a);
}
//乘上b
int sum=0;
for(int i=1;i<=len;i++)
sum=( sum+ (long long)ans[len][i]*b[i]%mod )%mod;
return sum;
}
int main()
{
int n;
scanf("%d %d",&len,&n);
f[0]=1;
for(int i=1;i<=10;i++)
for(int j=1;j<=len&&j<=i;j++)
f[i]=( f[i]+f[i-j])%mod;
if(n<=10) printf("%d\n",f[n]);
else printf("%d\n",fast(n));
return 0;
}