Vijos 1067Warcraft III 守望者的煩惱(動態規劃+矩陣快速冪)

背景

守望者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個監獄的方案種數可以由下面遞推式得出:
f(n)=f(n1)+...+f(nk) {f{ \left( {n} \right) } =f{ \left( {n-1} \right) } +...+f{ \left( {n-k} \right) } }
由於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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章