【2018/10/11测试T2】坐标系

【题目】

传送门

题目描述:

Tom 者表也,数学者景也,表动则景随矣。
Tom 不喜欢数学,可数学却待 Tom 如初恋,Tom 睡觉的时候也不放过。
Tom 的梦境中出现了一个平面直角坐标系,自原点,向四方无限延伸。
Tom 在坐标系的原点,他可以向上、向左或者向右走。他可以走 n n n 步,但不能经过相同的点。
Tom 想知道他有多少种走法

输入格式:

输入文件仅第一行一个正整数 n n n,表示 Tom 可以走的步数。

输出格式:

输出文件共一行,输出一个正整数,表示答案(对 1 0 9 + 7 10^9+7 109+7 取模)。

样例数据:

输入
2

输出
7

备注:

【输入输出样例 1 说明】
( 0 , 0 ) (0,0) (0,0) 出发走 2 2 2 步,共 7 7 7 种走法:
(0,0)->(0,1)->(0,2)
(0,0)->(0,1)->(1,1)
(0,0)->(0,1)->(-1,1)
(0,0)->(1,0)->(2,0)
(0,0)->(1,0)->(1,1)
(0,0)->(-1,0)->(-2,0)
(0,0)->(-1,0)->(-1,1)

【数据范围】

测试点编号 n n n
1 1 1 ~ 2 2 2 n &lt; = 10 n&lt;=10 n<=10
3 3 3 ~ 4 4 4 n &lt; = 100 n&lt;=100 n<=100
5 5 5 ~ 6 6 6 n &lt; = 1000 n&lt;=1000 n<=1000
7 7 7 ~ 8 8 8 n &lt; = 1 0 6 n&lt;=10^6 n<=106
9 9 9 ~ 10 10 10 n &lt; = 1 0 9 n&lt;=10^9 n<=109

【分析】

直接上题解吧,就不说暴力分了

考虑合法路径的特点,如果第 i − 1 i-1 i1 步向上走,那么第 i i i 步可以向上、左、右走;如果第 i − 1 i-1 i1 步向左走,那么第 i i i 步可以向上或者向左走;如果第 i − 1 i-1 i1 步向右走,那么第 i i i 步可以向上或者向右走。

我们用 f [ i ] [ 0 ] f[i][0] f[i][0] 表示走了 i i i 步,第 i i i 步向上走的方案数; f [ i ] [ 1 ] f[i][1] f[i][1] 表示走了 i i i 步,第 i i i 步向左走的方案数; f [ i ] [ 2 ] f[i][2] f[i][2] 表示走了 i i i 步,第 i i i 步向右走的方案数,递推方程:

f [ i ] [ 0 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] + f [ i − 1 ] [ 2 ] f[i][0]=f[i-1][0]+f[i-1][1]+f[i-1][2] f[i][0]=f[i1][0]+f[i1][1]+f[i1][2]
f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] f[i][1]=f[i-1][0]+f[i-1][1] f[i][1]=f[i1][0]+f[i1][1]
f [ i ] [ 2 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 2 ] f[i][2]=f[i-1][0]+f[i-1][2] f[i][2]=f[i1][0]+f[i1][2]

又因为 f [ i ] = f [ i ] [ 0 ] + f [ i ] [ 1 ] + f [ i ] [ 2 ] f[i]=f[i][0]+f[i][1]+f[i][2] f[i]=f[i][0]+f[i][1]+f[i][2],继续化简可以得到 f [ i ] = 2 ∗ f [ i − 1 ] + f [ i − 2 ] f[i]=2*f[i-1]+f[i-2] f[i]=2f[i1]+f[i2]

但是 n n n 最大有 1 0 9 10^9 109,直接 O ( n ) (n) (n) 递推还是不行,我们还要用到矩阵快速幂来优化,这样就降成了 O ( l o g &ThickSpace; n ) (log \; n) (logn),就可以 A 了

这道题的关键点:找递推式以及用矩阵快速幂来优化递推


【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10
#define mod 1000000007
using namespace std;
struct matrix
{
	int m[N][N];
	matrix(int t=0)
	{
		memset(m,0,sizeof(m));
		for(int i=1;i<=2;++i)  m[i][i]=t;
	}
	friend matrix operator * (const matrix &a,const matrix &b)
	{
		int i,j,k;
		matrix c(0);
		for(i=1;i<=2;++i)
		  for(j=1;j<=2;++j)
		    for(k=1;k<=2;++k)
		      c.m[i][j]=(c.m[i][j]+1ll*a.m[i][k]*b.m[k][j]%mod)%mod;
		return c;
	}
	friend matrix operator ^ (matrix a,int b)
	{
		matrix c(1);
		for(;b;b>>=1,a=a*a)
		  if(b&1)
		    c=c*a;
		return c;
	}
}A,B;
void init()
{
	A.m[1][1]=7;A.m[1][2]=3;
	A.m[2][1]=0;A.m[2][2]=0;
	B.m[1][1]=2;B.m[1][2]=1;
	B.m[2][1]=1;B.m[2][2]=0;
}
int main()
{
//	freopen("coordinate.in","r",stdin);
//	freopen("coordinate.out","w",stdout);
	int n;
	scanf("%d",&n);
	init();
	if(n==1)  {printf("3");return 0;}
	if(n==2)  {printf("7");return 0;}
	matrix C=A*(B^(n-2));
	printf("%d",C.m[1][1]);
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值