Codeforces Beta Round #93 (Div. 1 Only) D. Fibonacci Sums

首先把一个数n表示成若干个菲波拉契数的和。我们这样寻找这些菲波拉契数

while(n!=0)

{

    找到不大于n的最大的菲波拉契数K

    n=n-K

}

对于第i菲波拉契数,如果在我们找到的数种,就把第i位置1,否则置0,那么我们就可以用一个二进制数表示一个数了(这种方法在《具体数学》中菲波拉契数列那一章中有所提及)

比如

1=1

2=10 

3=100

4=101  (3+1)

比如我们有如下数: 100011,由菲波拉契数的性质我们知道,可以把一个的i位的1去掉,把i-1和i-2位都加1,那么就有:

100011=011011

同理也可以变回去

100011=100100

因为我们找这些数的时候总是找最大的那个菲波拉契数,所以可以不用考虑第二种情况。(我们最后的二进制表示中不会出现连续的1)

现在只考虑把1个1变成2个1的情况。如果一个1后面有连续i个0,那么就有i/2下整中变法,比如

100000=011000=01011

而且变过之后,原本是1的位置成为了0

其实我们构造的二进制数中有n个1,且第i个1后面紧跟连续a[i]个0,i从右往左数

比如数为:1001000110

a数组为:1,0,3,2

用dp[i][0]表示考虑前i个,把第i个1变为0有多少种变法,dp[i][1]表述不把第i个1变0有多少种变法

dp[i][1]=dp[i-1][0]+dp[i-1][1]  

dp[i][0]=dp[i-1][0]*((a[i]+1)/2)+dp[i-1][1]*(a[i]/2) 第一项中因为i-1项变了0,所以i项后面连续的0多了一个。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
#include <cmath>
#include <iostream>
#define maxn 509
#define INF 1e6
using namespace std;
__int64 fib[maxn],n;
__int64 dp[maxn][2];
bool vis[100];int a[maxn];
int bin(__int64 x)
{
	int L=1,R=90;
	while(L<R)
	{
		int M=(L+R+1)>>1;
		if(fib[M]<=x)
			L=M;
		else
			R=M-1;
	}
	return L;
}
int main()
{
	fib[1]=1;fib[2]=2;
	for(int i=3;i<=90;i++)
	{
		fib[i]=fib[i-1]+fib[i-2];
	}
	int tt;
	scanf("%d",&tt);
	while(tt--)
	{
		scanf("%I64d",&n);
		memset(vis,0,sizeof(vis));
		int mx=0,tot=0;
		while(n)
		{
			int pos=bin(n);
			mx=max(mx,pos);
			n-=fib[pos];
			vis[pos]=1;
		}
		int cnt=0;
		for(int i=1;i<=mx;i++)
		{
			if(!vis[i])
				cnt++;
			else
			{
				a[tot++]=cnt;
				cnt=0;
			}
		}
		dp[0][0]=a[0]/2;dp[0][1]=1;
		for(int i=1;i<tot;i++)
		{
			dp[i][0]=dp[i-1][1]*(a[i]/2)+dp[i-1][0]*((a[i]+1)/2);
			dp[i][1]=dp[i-1][1]+dp[i-1][0];
		}
		printf("%I64d\n",dp[tot-1][0]+dp[tot-1][1]);
	}
	//system("pause");
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值