title
OJ 1352
Vijos 1067
背景 Background
守望者-warden,长期在暗夜精灵的的首都艾萨琳内担任视察监狱的任务,监狱是成长条形的,守望者warden拥有一个技能名叫“闪烁”,这个技能可以把她传送到后面的监狱内查看,她比较懒,一般不查看完所有的监狱,只是从入口进入,然后再从出口出来就算完成任务了。
描述 Description
头脑并不发达的warden最近在思考一个问题,她的闪烁技能是可以升级的,k级的闪烁技能最多可以向前移动k个监狱,一共有n个监狱要视察,她从入口进去,一路上有n个监狱,而且不会往回走,当然她并不用每个监狱都视察,但是她最后一定要到第n个监狱里去,因为监狱的出口在那里,但是她并不一定要到第1个监狱。
守望者warden现在想知道,她在拥有k级闪烁技能时视察n个监狱一共有多少种方案?
输入格式 Input Format
第一行是闪烁技能的等级k(1<=k<=10)
第二行是监狱的个数n(1<=n<=2^31-1)
输出格式 Output Format
由于方案个数会很多,所以输出它 mod 7777777后的结果就行了
样例输入 Sample Input
2
4
样例输出 Sample Output
5
时间限制 Time Limitation
各个测试点1s
注释 Hint
把监狱编号1 2 3 4,闪烁技能为2级,
一共有5种方案
→1→2→3→4
→2→3→4
→2→4
→1→3→4
→1→2→4
小提示:建议用int64,否则可能会溢出
来源 Source
杜杜我爱你个人原创
analysis
根据题意我们可以得到这样一个状态转移方程:
这个方程很简单,但是由于复杂度为 ,对于这道题来说,无法接受,那我们只好想怎么优化了,我们可以发现,当 等于二的时候,这就是个斐波那契数列,ok,那我们就可以借鉴斐波那契数列的优化:矩阵乘法来优化这道题了。
我们设这个矩阵为A,那么显然可以得到以下的关系:
那么只要可以得到A,我们就能在
的复杂度内完成此题。
下面给出矩阵A,因为我也不知道为什么,具体的看上篇博文的链接吧。
for (int i=1; i<=K; ++i)
num[i][1]=1;
for (int i=2; i<=K; ++i)
num[i-1][i]=1;
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=7777777;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
ll n,K;
struct martix
{
ll num[15][15];
inline void build()
{
memset(num,0,sizeof(num));
for (int i=1; i<=K; ++i)
num[i][1]=1;
for (int i=2; i<=K; ++i)
num[i-1][i]=1;
}
};
inline martix mul(martix a,martix b)
{
martix ans;
memset(ans.num,0,sizeof(ans));
for (int i=1; i<=K; ++i)
for (int j=1; j<=K; ++j)
for (int k=1; k<=K; ++k)
(ans.num[i][j]+=a.num[i][k]*b.num[k][j])%=mod;
return ans;
}
inline void Quick_power(ll p)
{
martix ans,a;
a.build();
memset(ans.num,0,sizeof(ans));
for (int i=1; i<=K; ++i)
ans.num[i][i]=1;
while (p)
{
if (p&1)
ans=mul(ans,a);
a=mul(a,a);
p>>=1;
}
printf("%lld\n",ans.num[1][1]);
}
int main()
{
read(K);read(n);
Quick_power(n);
return 0;
}