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;
}