Problem
每一个正整数都可以表示为若干个斐波那契数的和,一个整数可能存在多种不同的表示方法,例如:14 = 13 + 1 = 8 + 5 + 1,其中13 + 1是最短的表示(只用了2个斐波那契数)。定义F(n) = n的最短表示中的数字个数,F(14) = 2,F(100) = 3(100 = 3 + 8 + 89),F(16) = 2(16 = 8 + 8 = 13 + 3)。定义G(n) = F(1) + F(2) + F(3) + ...... F(n),G(6) = 1 + 1 + 1 + 2 + 1 + 2 = 8。给出若干个数字n,求对应的G(n)。
Solution
以fib个为一组找规律,发现当前fib[i]个数是前fib[i-1]个数照搬,然后前前fib[i-2]个数搬过来再+1,于是预处理fib和fib前缀和,还有sum和sum前缀和,sum[i]代表一组数,有fib[i]个,他们的F之和,然后二分找到前面成组的数,不成组的按照规律,递归求解。
Code
#include<stdio.h>
#include<set>
#include<iostream>
#include<stack>
#include<cstring>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<algorithm>
typedef long long ll;
typedef long double ld;
typedef double db;
#define io_opt ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int mod=1e9+7;
inline int mo(ll a,int p){
return a>=p?a%p:a;
}
inline int rd() {
int x = 0, f = 1;
char ch;
while (ch < '0' || ch > '9') {
if (ch == '-')f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return f * x;
}
inline ll gcd(ll x, ll y){
return y==0?x:gcd(y,x%y);
}
inline ll speed(ll a,ll b){
ll cur=a,anss=1;
while(b){
if(b&1) anss=anss*cur;
cur=cur*cur;
b>>=1;
}
return anss;
}
const int MAXN=1e5;
bool ipr[MAXN+20];
int cnt,pri[MAXN/5];
void prime(){//埃式筛法
int N=sqrt(MAXN)+0.5,mul;
memset(ipr,true,sizeof(ipr));
ipr[1]=false;
for(int i=2;i<=N;i++){
if(ipr[i]==true){
i==2?mul=1:mul=2;
for(int j=i*i;j<=MAXN;j+=i*mul){
ipr[j]=false;
}
}
}
for(int i=2;i<=MAXN;i++){
if(ipr[i]==true){
pri[++cnt]=i;
}
}
}
int T;
ll n;
ll fib[120]={1,1},sfib[120]={0,1};
ll sum[120]={0,1,3},ssum[120]={0,1,4};
ll dfs(ll layer,ll cur){
if(layer<=0||cur<=0) return 0;
if(cur==fib[layer]) return sum[layer];
if(cur<=fib[layer-1]) return dfs(layer-1,cur);
return sum[layer-1]+dfs(layer-2,cur-fib[layer-1])+cur-fib[layer-1];
}
int main(){
//io_opt;
for(int i=2;i<=91;i++){
fib[i]=fib[i-1]+fib[i-2];
sfib[i]=sfib[i-1]+fib[i];
}
for(int i=3;i<=91;i++){
sum[i]=sum[i-1]+sum[i-2]+fib[i-2];
ssum[i]=ssum[i-1]+sum[i];
//81>0
}
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
n--;
ll ans=1,cur=n;
ll p=lower_bound(sfib+1,sfib+1+81,cur)-sfib-1;
//cout<<"!"<<p<<endl;
cur-=sfib[p];
ans+=ssum[p];
p++;
ans+=dfs(p,cur);
printf("%lld\n",ans);
}
return 0;
}