D. Fibonacci进制(找规律)
题目链接: Fibonacci进制
题意
Fibonacci数是非常有名的一个数列,它的公式为 f(n)=f(n-1)+f(n-2),f(0)=1,f(1)=2。
我们可以把任意一个数x表示成若干不相同的Fibonacci数的和, 比如说14 = 13+1 = 8+5+1 = 8+3+2+1。
如果把Fibonacci数列作为数的位权,即f(i)作为第i位的位权,每位的系数只能是0或者1,从而得到一个01串。 比如14可以表示成 100001,11001,10111。 我们再把这个01串看成2进制,再转成10进制以后就变成了 33,25,23。 为了避免歧义,我们将使用最小的那个值23。
请按照这个过程计算一下10进制整数通过上述转换过程得到的10进制整数。
思路
先说方法,就是先预处理斐波那契,数组大小有45吧,毕竟这也近似于指数增长。然后贪心的从最高位向下扫,如果这个数小于等于要输入的数n,那么就标记一下,并且n减去这个数。这样从最高位扫到最低位。
这样我们就得到了一个最大的01串,接下来就简单了,就是从第一位开始扫,当出现了两个0并且又出现了1后,就把那两个0换成1,原来的1换成0,注意这里可能要重复多次。
现在就讲解一下,为什么一直取最大可取的数就能组合成一个最大的01串。
这里有个不知道怎么证明的规律,就是每个数都能由斐波那契数组合成,所以,当这个数减去最大可减的数时,剩下的数就一定能够被剩下的斐波那契数组合出来。
代码
#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = (int)(j);i <= (int)(k);i ++)
#define per(i,j,k) for(int i = (int)(j);i >= (int)(k);i --)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
typedef long long ll;
const int INF = (int)0x3f3f3f3f;
const int MAXN = (int)1e6+7;
ll Fi[50];
ll sub[50];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
Fi[2] = 2;
Fi[1] = 1;
rep(i,3,45){
Fi[i] = Fi[i-1]+Fi[i-2];
}
ll T;
cin >> T;
while (T --){
ll n;
cin >> n;
mmm(sub,0);
per(i,45,1){
if (n >= Fi[i]){
sub[i] = 1;
n -= Fi[i];
}
}
rep(i,1,45){
ll t = i;
while (t >= 3 && sub[t-1] == 0 && sub[t-2] == 0 && sub[t] == 1){
sub[t-1] = sub[t-2] = 1;
sub[t] = 0;
t = t-2;
}
}
ll flag = 0;
ll ans = 0;
per(i,45,1){
if (sub[i])flag = 1;
if (flag) {
if (sub[i])ans = ans<<1|1;
else ans = ans<<1;
}
}
cout << ans << endl;
}
}