Vijos 1067 守望者的烦恼 矩阵乘法优化DP

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/88835426

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

根据题意我们可以得到这样一个状态转移方程:
f [ i ] = j = i 1 i k f [ j ] f\left[ i\right] =\sum ^{i-k}_{j=i-1}f\left[ j\right]

这个方程很简单,但是由于复杂度为 O ( N K ) O(NK) ,对于这道题来说,无法接受,那我们只好想怎么优化了,我们可以发现,当 K K 等于二的时候,这就是个斐波那契数列,ok,那我们就可以借鉴斐波那契数列的优化:矩阵乘法来优化这道题了。

我们设这个矩阵为A,那么显然可以得到以下的关系:
( f [ N ] f [ N k ] ) A = ( f [ N + 1 ] f [ N k + 1 ] ) A \left( f\left[ N\right] \ldots f\left[ N-k\right] \right) \ast A=\left( f\left[ N+1\right] \ldots f\left[ N-k+1\right] \right) \ast A
那么只要可以得到A,我们就能在 O ( log N ) O(\log N) 的复杂度内完成此题。
下面给出矩阵A,因为我也不知道为什么,具体的看上篇博文的链接吧。

for (int i=1; i<=K; ++i)
	num[i][1]=1;
for (int i=2; i<=K; ++i)
	num[i-1][i]=1;

然后用快速幂优化,最后 A N . n u m [ 1 ] [ 1 ] {A^N}.num[1][1] 就是本题答案。
——来自 C y d i a t e r Cydiater

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;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/88835426
今日推荐