vijos 1067 Warcraft 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,否则可能会溢出

来源

杜杜我爱你个人原创


参考资料:传送门1传送门2传送门3

很容易想到递推式为f[i]=f[i-1]+f[i-2]+......+f[i-k]

然而由于n太大需要用矩乘优化

其对应矩阵的构造方法为:

在右上角的(n-1)*(n-1)的小矩阵中的主对角线上填1,矩阵第n行填对应的系数,其它地方都填0。

例如,我们可以用下面的矩阵乘法来计算f(n) = 4f(n-1) - 3f(n-2) + 2f(n-4)的第k项:

(2a-3c+4d即为f[n])



(该构造方法摘自传送门1)

这题就解决了

#include<cstdio>
#include<cstring>
const int mod=7777777,N=13;
int len;
inline void calc(int a[N][N],int b[N][N])
{
	int c[N][N];
	for(int i=1;i<=len;i++)
	for(int j=1;j<=len;j++)
	{
		c[i][j]=0;
		for(int k=1;k<=len;k++)
		c[i][j]=( c[i][j]+ (long long)a[i][k]*b[k][j]%mod )%mod;
	}
	for(int i=1;i<=len;i++)
	for(int j=1;j<=len;j++)
	a[i][j]=c[i][j];
}
int f[N];
int fast(int n)
{
	//自乘 
	int ans[N][N],a[N][N],b[N];
	memset(ans,0,sizeof(ans));
	memset(a,0,sizeof(a));
	for(int i=1;i<=len;i++)	ans[i][i]=1;
		
	for(int i=2;i<=len;i++)	a[i-1][i]=1;
	for(int i=1;i<=len;i++)	a[len][i]=1;
	n-=10;
	for(int i=len;i>=1;i--)	b[len-i+1]=f[11-i];	
	while(n)
	{
		if(n&1)	calc(ans,a);
		n=n>>1;
		calc(a,a);
	}
	//乘上b
	int sum=0;
	for(int i=1;i<=len;i++)
	sum=( sum+ (long long)ans[len][i]*b[i]%mod )%mod;
	return sum;
	 
}
int main()
{
	int n;
	scanf("%d %d",&len,&n);
	f[0]=1;
	for(int i=1;i<=10;i++)
	for(int j=1;j<=len&&j<=i;j++)
	f[i]=( f[i]+f[i-j])%mod;
	if(n<=10)	printf("%d\n",f[n]);
	else printf("%d\n",fast(n));
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值