String Game(动态规划+矩阵快速幂)

样例输入:

2

 样例输出:

2

中文题意:

有一款玩字符串S的有趣的单人游戏。S的长度为n,且只包含字符12。玩家从字符串的起始位置(index = 0)开始。每一步,玩家要选择从当前位置向左或是向右移动,而移动的距离就是当前位置S[index]的值。例如,当S[index] = 1,则这一步结束后,玩家的位置可以是index + 1 或 index - 1;若S[index] = 2,则这一步结束后,玩家的位置可以是index + 2 或 index - 2。当然,玩家不能移动到字符串外面,也就是说,index总是满足 0≤index≤n-1。玩家获胜,当且仅当玩家能够访问字符串S中的每个位置刚好1次,并且停在最后一个位置(n-1)。给定n,请计算玩家在最佳情况下可以获胜的字符串数量。因为数量可能非常大,请把它对1000000007取模。

输入格式(Input Specification):
每个输入包含一个测试用例。输入只有一行,是字符串的长度n(1≤n≤2×109)。

输出格式(Output Specification):
在一行中输出答案。

样例提示(Hint):
这两个字符串是1112

分析:这道题目需要先尝试模拟几个样例,假如我们设长度为n的时候的答案为f[n],我们尝试进行方程的推导。那么f[n]/2代表到达第n个点,不考虑第n个点的值的方案数。那么我们可以从第n-1个点走一步直接到达,那么也就是f[n-1]/2,因为这个时候第n-1个点的取值只能是1,我们从第n-1个点不经过第n-1个点走到第n个点只能走两步,但因为我们没法回去,所以这种情况是不符合题意的。接下来就是从第n-3个点,肯定不能经过一步走到n-2个点,因为我们刚才已经分析出来从第n-2个点是无法直接不经过第n-1个点到达第n个点的,所以这里我们只能走两步到第n-1个点,然后再回退一步到第n-2个点,然后再一次走两步到达第n个点,这样的话方案数也是f[n-3]/2,因为从第n-3到第n-1个点的数已经确定,所以答案就是f[n]/2=f[n-1]/2+f[n-3]/2,那么也就是f[n]=f[n-1]+f[n-3],所以这个过程我们就可以用矩阵快速幂加速递推。递推方程如下:

[fn,fn-1,fn-2]=[fn-1,fn-2,fn-3]*矩阵e

矩阵e为:

1 1 0

0 0 1

1 0 0

那么[fn,fn-1,fn-2]=[f3,f2,f1]*e^(n-3),我们直接用快速幂求出e^(n-3),然后直接一个矩阵乘即可得到结果。

细节见代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int N=1e5+10,mod=1e9+7;
struct mat{
	int a[4][4];
	int h,w;
}e,ans;
mat mul(mat a,mat b)
{
	mat c;
	c.h=a.h;
	c.w=b.w;
	memset(c.a,0,sizeof (c.a));
	for(int k=1;k<=a.w;k++)
	for(int i=1;i<=c.h;i++)
	for(int j=1;j<=c.w;j++)
		c.a[i][j]=(c.a[i][j]+1ll*a.a[i][k]*b.a[k][j])%mod;
	return c;	
}
int main()
{
	int n;
	cin>>n;
	if(n<=3)
	{
		printf("2");
		return 0;
	}
	e.w=e.h=3;
	e.a[1][1]=e.a[1][2]=e.a[2][3]=e.a[3][1]=1;
	ans.w=ans.h=3;
	ans.a[1][1]=ans.a[2][2]=ans.a[3][3]=1;
	n-=3;
	while(n)
	{
		if(n&1) ans=mul(ans,e);
		e=mul(e,e);
		n>>=1;
	}
	mat b;
	b.h=1;b.w=3;
	b.a[1][1]=b.a[1][2]=b.a[1][3]=2;
	b=mul(b,ans);
	printf("%d",b.a[1][1]);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值