【矩阵乘法】守望者的烦恼

背景 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 
 
   
   
  样例输出 Sample Output 
 
   
   
  时间限制 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 
 杜杜我爱你个人原创
  


这道题主要要搞清楚题意。

也就是说,第一步只能到达1~k的房间,因为第一步就要闪烁。

所有就是求从1、2、3、4…k到n的方案总数。


f[i] = f[0]+f[1]+...+f[i-1] (i <= k)

f[i] = f[i-k]+f[i-k+1]+...+f[i-1] (i > k)


这个可以用矩阵乘法,不过明显1到k需要手动算,因为方程不一样。


注意:同余定理也适用于矩阵。。。(不知道为什么,但是我没有一直取余就会爆,还好试了一组大数据)

另外,7777777*7777777+7777777会爆long int。所以要用64位长整。

还有,为了安全起见,矩阵要赋初值0(不是指空矩阵,空矩阵是主对角线为1),不然在乘法时肯定会出错。


#include <cstdio>
#include <string>
#include <cstring>

typedef long long ll;

struct Rect
{
	ll r[13][13];
	Rect(){memset(r,0,sizeof r);}
	void operator=(const Rect&r2){memcpy(r,r2.r,sizeof r);}
};

Rect mult;
Rect ans;
long k,n;

void multiply(Rect &r1,const Rect &r2)
{
	Rect tmp;
	for (long i=1;i<k+1;i++)
	{
		for (long j=1;j<k+1;j++)
		{
			for (long l=1;l<k+1;l++)
			{
				tmp.r[i][j] = (tmp.r[i][j]+r1.r[i][l]*r2.r[l][j])%7777777;
			}
		}
	}
	r1 = tmp;
}

void quickpower(long a)
{
	while (a)
	{
		if (a&1){multiply(ans,mult);}
		multiply(mult,mult);
		a >>= 1;
	}
}

long f[15];

int main()
{
	freopen("warden.in","r",stdin);
	freopen("warden.out","w",stdout);
	scanf("%ld%ld",&k,&n);

	f[0] = 1;
	for (long i=1;i<k+1;i++)
	{
		for (long j=i-1;j>-1&&i-j<=k;j--)
		{
			f[i] += f[j];
		}
	}

	if (n <= k)
	{
		printf("%ld",f[n]);
		return 0;
	}

	for (long i=1;i<k;i++)
		mult.r[i][i+1] = 1;
	for (long i=1;i<k+1;i++)
		mult.r[k][i] = 1;
	for (long i=1;i<k+1;i++)
		ans.r[i][i] = 1;

	quickpower(n-k);

	ll out = 0;

	for (long i=1;i<k+1;i++)
	{
		out = (out + f[i]*ans.r[k][i])%7777777;
	}

	printf("%ld",out);
	return 0;
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值