背景
守望者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級閃爍技能時視察n個監獄的方案種數可以由下面遞推式得出:
由於n的範圍過大,我們可以用矩陣的快速冪方法求得。由遞推式可以構造下面的等式:
得到上面的等式就可以很容易的看出解題思路了:我們先初始化f(0)到f(k-1)的值,再計算左邊方陣的n次方,再相乘。結果的第一個值即爲所求。
AC代碼
#include <bits/stdc++.h>
#define FOR(I,A,B) for(int I = (A); I < (B); I++)
#define FORE(I,A,B) for(int I = (A); I <= (B); I++)
#define PRII pair<int,int>
#define INF 0x3f3f3f3f
#define MOD 7777777
using namespace std;
long long n,k;
long long c[12];
struct M{
long long m[12][12];
}ori;
void print(M t){
FOR(i,0,k){
FOR(j,0,k){
printf("%lld ",t.m[i][j]);
}printf("\n");
}
}
M mul(M a,M b){
M res;
FOR(i,0,k){
FOR(j,0,k){
long long tmp=0;
FOR(l,0,k) tmp+=a.m[i][l]*b.m[l][j];
tmp=tmp%MOD;
res.m[i][j]=tmp;
}
}
return res;
}
M dg(M t,long long num){
M res;
if(num==1){
res=t;
}else if(num==2){
res=mul(t,t);
}else if(num%2){
M tmp=dg(t,num/2);
M tmp2=mul(tmp,ori);
res=mul(tmp,tmp2);
}else{
M tmp=dg(t,num/2);
res=mul(tmp,tmp);
}
return res;
}
int main()
{
cin>>k>>n;
FOR(i,0,k-1) ori.m[i][i+1]=1;
FOR(i,0,k) ori.m[k-1][i]=1;
long long tt=1;
c[0]=1;
FOR(i,1,k){
c[i]=tt;
tt+=c[i];
}
M rr=dg(ori,n);
long long res=0;
FOR(i,0,k) res+=rr.m[0][i]*c[i];
cout<<res%MOD<<endl;
return 0;
}