NOIP模拟 坐标系(找规律+矩阵乘法优化)

【题目描述】

Tom 者表也,数学者景也,表动则景随矣。

Tom 不喜欢数学,可数学却待 Tom 如初恋,Tom 睡觉的时候也不放过。

Tom 的梦境中出现了一个平面直角坐标系,自原点,向四方无限延伸。

Tom 在坐标系的原点,他可以向上、向左或者向右走。他可以走 n 步,但不能经过相同 的点。

Tom 想知道他有多少种走法

【输入格式】

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

【输出格式】

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

【样例输入】

2

【样例输出】

7

【备注】

测试点编号 n
1~2 n<=10
3~4 n<=100
5~6 n<=1000
7~8 n<=10^6
9~10

n<=10^9

 

【题目分析】

很有意思的一道推理题。

首先我们发现,因为只有三个方向:上,左,右,那么对于上,那么就不会有限制,因为不可能向下走回去,他的方案数就是n-1的方案数。

接着对于左右两种走法,我们发现他可以走的形状如下图:

这是向左走,向右走就是将图像镜像一下即可,两边合起来有多少种走法呢?因为一边一半,最高的点多算了两次,所以最后的结果刚好是n-1的方案数+n-2的方案数。所以可以得到结论,f[n]=2*f[n-1]+f[n-2]。

然后考虑优化,因为O(n)的复杂度肯定是过不了的,那么可以参照之前的一道题:传送门:https://blog.csdn.net/g21glf/article/details/82290499这道题求斐波那契数列的方法就是矩阵快速幂优化,可以发现,如果我们用

表示第一个矩阵,那么只要我们乘上一个

我们就可以得到下一对fi,因为矩阵乘法满足结合律,所以我们可以先利用矩阵快速幂求出后面这个矩阵的乘积,然后就可以得到答案了。

【代码~】

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MOD=1e9+7;

LL n;
LL Read()
{
	LL i=0,f=1;
	char c;
	for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')
	  f=-1,c=getchar();
	for(;c>='0'&&c<='9';c=getchar())
	  i=(i<<3)+(i<<1)+c-'0';
	return i*f;
}

struct martix{
	LL a[2][2];
	martix(int t=0)
	{
		memset(a,0,sizeof(a));
		a[0][0]=a[1][1]=t;
	}
	friend inline martix operator+(const martix &x,const martix &y){
		martix c;
		for(int i=0;i<2;++i)
		  for(int j=0;j<2;++i)
		    c.a[i][j]=x.a[i][j]+y.a[i][j];
		return c;
	}
	friend inline martix operator*(const martix &x,const martix &y){
		martix c;
		for(int i=0;i<2;++i)
		  for(int k=0;k<2;++k)
		  	for(int j=0;j<2;++j)
		  	  c.a[i][j]=(c.a[i][j]+x.a[i][k]*y.a[k][j])%MOD;
		return c;
	}
	friend inline martix operator^(martix aa,LL b)
	{
		martix c(1);
		for(;b;b>>=1,aa=aa*aa) 
		  if(b&1) 
		    c=c*aa;
		return c; 
	}
}base,ans;

int main()
{
	cin>>n;
	ans.a[0][0]=1,ans.a[0][1]=1,ans.a[1][0]=0,ans.a[1][1]=0;
	base.a[0][0]=0,base.a[0][1]=1,base.a[1][0]=1,base.a[1][1]=2;
/*	for(int i=1;i<=10;++i)
	{
		for(int j=0;j<2;++j)
		{
			for(int k=0;k<2;++k)
			  cout<<(base^i).a[j][k]<<" ";
			cout<<endl;
		}
		cout<<endl;
	}
	*/
	martix x=base^n;
	cout<<(x.a[0][1]+x.a[1][1])%MOD;        //也可用ans矩阵去乘,取乘后的[0][1]的值
	return 0;
}

 

posted @ 2018-10-11 14:15  Ishtar~  阅读(248)  评论(0编辑  收藏  举报