【思路】
先考虑如何计算
F
(
n
)
F(n)
F(n) ,如果
n
n
n 是斐波那契数,那么
F
(
n
)
=
1
F(n)=1
F(n)=1 ,否则尽量把
n
n
n 减去最大可减的斐波那契数,并重复这一过程,也就是
F
(
n
)
=
{
n
=
1
,
n
是fib数
F
(
n
−
m
)
+
1
,
n
不是fib数
F(n)=\begin{cases} n=1, & \text {$n$是fib数} \\ F(n-m)+1, & \text{$n$ 不是fib数} \end{cases}
F(n)={n=1,F(n−m)+1,n是fib数n 不是fib数 根据
F
(
n
)
F(n)
F(n) 的计算公式再来考虑计算它的前缀和
G
(
n
)
G(n)
G(n),如果
n
n
n 是斐波那契数,假设
n
=
f
i
b
[
i
]
n=fib[i]
n=fib[i],则
G
(
f
i
b
[
i
]
)
−
G
(
f
i
b
[
i
−
1
]
)
=
F
(
f
i
b
[
i
−
1
]
+
1
)
+
F
(
f
i
b
[
i
−
1
]
+
2
)
+
.
.
.
+
F
(
f
i
b
[
i
]
−
1
)
+
F
(
f
i
b
[
i
]
)
G(fib[i])-G(fib[i-1])=F(fib[i-1]+1)+F(fib[i-1]+2)+...+F(fib[i]-1)+F(fib[i])
G(fib[i])−G(fib[i−1])=F(fib[i−1]+1)+F(fib[i−1]+2)+...+F(fib[i]−1)+F(fib[i]) 根据
F
F
F 的计算公式,等式右端
=
(
F
(
1
)
+
1
)
+
(
F
(
2
)
+
1
)
+
.
.
.
+
(
F
(
f
i
b
[
i
]
−
1
−
f
i
b
[
i
−
1
]
)
+
1
)
+
F
(
f
i
b
[
i
]
)
=(F(1)+1)+(F(2)+1)+...+(F(fib[i]-1-fib[i-1])+1)+F(fib[i])
=(F(1)+1)+(F(2)+1)+...+(F(fib[i]−1−fib[i−1])+1)+F(fib[i])
=
F
(
1
)
+
F
(
2
)
+
.
.
.
+
F
(
f
i
b
[
i
]
−
f
i
b
[
i
−
1
]
−
1
)
+
f
i
b
[
i
]
−
f
i
b
[
i
−
1
]
=
G
(
f
i
b
[
i
−
2
]
−
1
)
+
f
i
b
[
i
−
2
]
=F(1)+F(2)+...+F(fib[i]-fib[i-1]-1)+fib[i]-fib[i-1]=G(fib[i-2]-1)+fib[i-2]
=F(1)+F(2)+...+F(fib[i]−fib[i−1]−1)+fib[i]−fib[i−1]=G(fib[i−2]−1)+fib[i−2]
移项可得
G
(
f
i
b
[
i
]
)
=
G
(
f
i
b
[
i
−
1
]
)
+
G
(
f
i
b
[
i
−
2
]
−
1
)
+
f
i
b
[
i
−
2
]
G(fib[i])=G(fib[i-1])+G(fib[i-2]-1)+fib[i-2]
G(fib[i])=G(fib[i−1])+G(fib[i−2]−1)+fib[i−2]
同理当
n
n
n 不是斐波那契数时,也用相同方法递推,可以得到
G
(
n
)
=
G
(
m
)
+
G
(
n
−
m
)
+
(
n
−
m
)
,
m
是
小
于
n
的
最
大
的
f
i
b
数
G(n)=G(m)+G(n-m)+(n-m),m是小于n的最大的fib数
G(n)=G(m)+G(n−m)+(n−m),m是小于n的最大的fib数
在计算时可以提前预处理出
G
(
f
i
b
[
i
]
)
G(fib[i])
G(fib[i]) 的值,直接递归计算会超时
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=90;
ll f[maxn];//fib序列 1,1,2,3,5,8,13...
ll g[maxn];//记忆化 g[i]=G(f[i]) 提高效率
ll G(ll n){
if(n<=2) return n;
int p=lower_bound(f,f+maxn,n)-f;
if(f[p]==n){
if(g[p]!=-1) return g[p];
return g[p]=G(f[p-1])+G(f[p-2]-1)+f[p-2];
}
else{
ll m=f[p-1];
return G(m)+G(n-m)+(n-m);
}
}
int main(){
f[0]=f[1]=1;
for(int i=2;i<maxn;++i) f[i]=f[i-1]+f[i-2];
for(int i=0;i<maxn;++i) g[i]=-1;
int T;
scanf("%d",&T);
while(T--){
ll n;
scanf("%lld",&n);
printf("%lld\n",G(n));
}
return 0;
}