题目
F(x)=1,x∈[0,4)
F
(
x
)
=
1
,
x
∈
[
0
,
4
)
F(x)=F(x−1)+F(x−π),x∈[4,+∞)
F
(
x
)
=
F
(
x
−
1
)
+
F
(
x
−
π
)
,
x
∈
[
4
,
+
∞
)
求
F(n)
F
(
n
)
。
n≤106
n
≤
10
6
。
题解
假设问题是问每次只加1或2,加到
n
n
的方案数。
则。
斐波那契?
没错。
用
O(n)
O
(
n
)
的另一种方法求,就是求
Σa+2b=nCaa+b
Σ
a
+
2
b
=
n
C
a
+
b
a
。
倒着推不好推?那就顺着推。
那么这道题目就将2替换为
π
π
,加到的目标数在区间
(n−4,n]
(
n
−
4
,
n
]
。
假设现在已经加到了
(n−4−π,n−4]
(
n
−
4
−
π
,
n
−
4
]
,则加
π
π
就可以达到目标。
假设现在已经加到了
(n−4−1,n−4]
(
n
−
4
−
1
,
n
−
4
]
,则加
1
1
就可以达到目标。
难点难在加到的目标数属于一个区间。
对于第一种情况,枚举加了多少个,则加的
1
1
的个数也确定了。你会发现除法有余数。这个余数的范围就对应着区间。
对于第二种情况,枚举加了多少个
1
1
,则加的的个数也确定了。你会发现除法有余数。这个余数的范围就对应着区间
(n−4−π,n−4]
(
n
−
4
−
π
,
n
−
4
]
。
所以就可以愉快地做题了。
心得
从大神们那里得知,问题 F(n)=F(n−a)+F(n−b) F ( n ) = F ( n − a ) + F ( n − b ) 都可以用时间复杂度为 O(n) O ( n ) 的算法求出答案。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1000010
#define DB double
#define LL long long
#define mo 1000000007
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const DB pi=acos(-1);
LL i,j,n,m,ans;
LL jc[N],ny[N];
LL ksm(LL x,LL y){
LL rs=1;
for(;y;y>>=1,x=(x*x)%mo)if(y&1)rs=(rs*x)%mo;
return rs;
}
void pre(){
LL i;
jc[0]=jc[1]=ny[0]=ny[1]=1;
fo(i,2,N-10)jc[i]=(jc[i-1]*i)%mo;
ny[N-10]=ksm(jc[N-10],mo-2);
fd(i,N-11,2)ny[i]=(ny[i+1]*(i+1))%mo;
}
LL C(LL n,LL m){return ((jc[n]*ny[m])%mo*ny[n-m])%mo;}
void add(LL &x,LL y){x=x+y>=mo?x+y-mo:x+y;}
int main(){
scanf("%lld",&n);
if(n<4){printf("1");return 0;}
pre();
fo(i,0,n-4){
j=(DB)(n-4-i)/pi;
add(ans,C(i+j,i));
}
m=(n-4)/pi;
fo(i,0,m){
j=(DB)(n-4-i*pi);
add(ans,C(i+j,i));
}
printf("%lld",ans);
return 0;
}