您的位置:首页 > 其它

【JZOJ4854】【NOIP2016提高A组集训第6场11.3】小澳的坐标系

2016-11-03 14:26 302 查看

题目描述

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

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

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

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

小澳想知道他有多少种走法。

数据范围测试点

1~2

n<=10

测试点3~4

n<=100

测试点5~6

n<=1000

测试点7~8

n<=10^6

测试点9~10

n<=10^9

解法

设f[i]为输入为i时,答案为f[i],设g[i]=f[i+1]-f[i]。

找规律获得g[i]=2*g[i-1]+g[i-2]。

矩阵乘法优化即可。

理性解法:

设f[i]为第i步走左的方案数,g[i]为第i步往右走的方案数,h[i]为第i步往上走的方案数;

显然:f[i]=f[i−1]+h[i−1],g[i]=g[i−1]+h[i−1],h[i]=f[i−1]+h[i−1]+g[i−1]

矩阵乘法优化即可。

代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define ll long long
using namespace std;
const char* fin="coordinate.in";
const char* fout="coordinate.out";
const ll inf=0x7fffffff;
const ll maxn=4,mo=1000000007;
ll n,i,j,k;
struct rect{
ll d[maxn][maxn];
rect(){
memset(d,0,sizeof(d));
}
void operator =(const rect &b){
ll i,j,k;
for (i=0;i<maxn;i++) for (j=0;j<maxn;j++) d[i][j]=b.d[i][j];
}
rect operator *(const rect &b){
rect c;
ll i,j,k;
for (i=0;i<maxn;i++)
for (j=0;j<maxn;j++)
for (k=0;k<maxn;k++){
c.d[i][j]=(c.d[i][j]+d[i][k]*b.d[k][j])%mo;
}
return c;
}

}a,b,ans;
rect power(rect a,ll v){
rect c;
bool bz=false;
while (v){
if (v&1){
if (!bz) c=a,bz=true;
else c=c*a;
}
a=a*a;
v>>=1;
}
return c;
}
int main(){
freopen(fin,"r",stdin);
freopen(fout,"w",stdout);
scanf("%lld",&n);
if (n==0) printf("1");
else if (n==1) printf("3");
else if (n==2) printf("7");
else{
a.d[0][0]=7;a.d[0][1]=3;a.d[0][2]=4;a.d[0][3]=2;
b.d[0][0]=b.d[0][1]=b.d[2][3]=b.d[3][0]=b.d[3][2]=1;
b.d[2][0]=b.d[2][2]=2;
a=a*power(b,n-2);
printf("%lld",a.d[0][0]);
}
return 0;
}


启发

观察题目得,当前方案数肯定由上一步的方案数得出。

尝试写出动态规划方程式,然后进行矩阵乘法优化邻近状态转移。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: