题目概述
每个数都可以用若干个斐波那契数组成,记录 F(x)=x 最少由多少个斐波那契数组成,求 G(n)=∑ni=1F(i) 。
解题报告
有个贪心: F(x)=F(x−fibmax)+1 ,其中 fibmax 是 ≤x 的最大斐波那契数(并不会严格证明QAQ)。
那么也就是说 F(x) 被斐波那契数分成一块一块了,那么我们可以得到:
- G(0)=0 。
- G(n)=G(n−fibmax)+∑fibmax−1i=0F(i)+(n−fibmax+1)
(提示:画图推一下就得到了, ∑fibmax−1i=0F(i) 其实由两部分( [fibmax,n] 和 [n−fibmax,fibmax] )组合而成。)
问题是 ∑fibmax−1i=0F(i)=G(fibmax−1) 怎么快速求出,我们按照上面的公式推一推:
G(fibmax−1)=G(fibmax−fibmax−1−1)+G(fibmax−1−1)+(fibmax−fibmax−1)=G(fibmax−2−1)+G(fibmax−1−1)+fibmax−2
所以这可以枚举斐波那契数很快预处理出来。
由于斐波那契数增长近似 2x ,所以效率近似 log2n 。
示例程序
#include<cstdio>
using namespace std;
typedef long long LL;
const int maxn=100;
int te;LL Tot,fib[maxn+5],n,sum[maxn+5];
void Make()
{
fib[0]=1;fib[1]=1;sum[0]=0;sum[1]=0;
for (Tot=1;fib[Tot]+fib[Tot-1]<=1e17;Tot++) fib[Tot+1]=fib[Tot]+fib[Tot-1];
for (int i=2;i<=Tot;i++) sum[i]=sum[i-1]+sum[i-2]+fib[i-2];
}
inline int Find(LL x)
{
int L=1,R=Tot;
while (L<=R)
{
int mid=L+(R-L>>1);
if (fib[mid]<=x) L=mid+1; else R=mid-1;
}
return R;
}
LL Count(LL n)
{
if (!n) return 0;int pos=Find(n);
return Count(n-fib[pos])+sum[pos]+(n-fib[pos]+1);
}
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
for (Make(),scanf("%d",&te);te;te--) scanf("%lld",&n),printf("%lld\n",Count(n));
return 0;
}