Description
【题目背景】
守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条行的,守望者warden拥有一个技能名叫“闪烁”,这个技能可以把她传送到后面的监狱内查看,她比较懒,一般不查看完所有的监狱,只是从入口进入,然后再从出口出来就算完成任务了。
【问题描述】
头脑并不发达的warden最近在思考一个问题,她的闪烁技能是可以升级的,k级的闪烁技能最多可以向前移动k个监狱,一共有n个监狱要视察,她从入口进去,一路上有n个监狱,而且不会往回走,当然她并不用每个监狱都视察,但是她最后一定要到第n个监狱里去,因为监狱的出口在那里,但是她并不一定要到第1个监狱。
守望者warden现在想知道,她在拥有k级闪烁技能时视察n个监狱一共有多少种方案?
Input
第一行是闪烁技能的等级k(1<=k<=10)
第二行是监狱的个数n(1<=n<=2^31-1)
Output
由于方案个数会很多,所以输出它 mod 7777777后的结果就行了
Sample Input
2
4
Sample Output
5
Hint
把监狱编号1 2 3 4,闪烁技能为2级,
一共有5种方案
→1→2→3→4
→2→3→4
→2→4
→1→3→4
→1→2→4
递推方程很裸,因为是加法原理:f[n]=f[n-k]+f[n-k+1]+...+f[n-1]
但是注意数据范围:监狱的个数n(1<=n<=2^31-1)
那么简单的for循环递推肯定是过不了的。
那么考虑用矩阵相乘来代替for循环的递推过程,因为矩阵(k*k)可以求快速幂,就可以把O(n)的枚举降到O(log2 n)了。
|0,1|
举个例子:若F(n)=F(n-1)+F(n-2),则 [F(4),F(5)]=[F(3),F(4)]* |1,1| ,那么如果要求到F(n),就相当于由初始矩阵乘以(n-k)个矩阵,矩阵快速幂,速度非常优秀。
再举个例子:
|0,0,1|
若F(n)=F(n-1)+F(n-2)+F(n-3),则有[F(7),F(8),F(9)]=[F(6),F(7),F(8)]* |1,0,1|
|0,1,1|
不难发现,这个矩阵就是matrix[i+1][i]=1,matrix[i][n]=1.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<cmath>
#define mod 7777777
using namespace std;
long long k,n,f[1000005]={0},rec[1000005]={0},mat[105][105]={0},temp[105][105]={0};
void xx(long long a[][105],long long b[][105])//矩阵乘法
{
long long c[105][105]={0};
for(long long i=1;i<=k;i++)
{
for(long long j=1;j<=k;j++)
{
for(long long q=1;q<=k;q++)
{
c[i][j]=(c[i][j]+a[i][q]*b[q][j])%mod;
}
}
}
memcpy(mat,c,sizeof(c));//把矩阵传回来
}
void PRE()
{
scanf("%d%d",&k,&n);
for(long long i=1;i<=k;i++)f[i]=1;
for(long long i=2;i<=k;i++)
for(long long j=1;j<i;j++)f[i]+=f[j];//初始化前k个初始矩阵,用朴素的for循环加法原理
for(long long i=1;i<k;i++)mat[i+1][i]=1;
for(long long i=1;i<=k;i++)mat[i][k]=1;//初始化矩阵
memcpy(temp,mat,sizeof(mat));
}
int main(){
PRE();
if(n==1)
{
printf("%d\n",k);
return 0;
}
if(k>=n)
{
printf("%d\n",f[n]);
return 0;
}//特判
n-=k;//初始矩阵是f[1...k],则要得到n,只需要乘(n-k)个矩阵。
while(n)
{
rec[++rec[0]]=n%2;
n/=2;
}
long long ans=0;
for(long long i=rec[0]-1;i>=1;i--)
{
xx(mat,mat);
if(rec[i]==1)xx(mat,temp);
}//快速幂,每一次都要翻番,遇到1就还要乘一个
for(long long i=1;i<=k;i++)
{
ans=(ans+f[i]*mat[i][k])%mod;//再把初始矩阵乘上去
}
printf("%d",ans);
return 0;
}