[Jzoj] 3850. Fibonacci进制

题目描述

在这里插入图片描述

题目解析

100 分 做 法 : D P 100 分做法:DP 100DP
N很大,先尝试几个小数据。可以发现,每个Fibonacci表示的长度,和Fibonacci数大小有关(1,2,3,5,8,13,21……),这些值最高位上是 1,后面全是 0,即第一个Fibonacci 表示长度为 i 的数是 fib[i]。因此按照长度对 Fibonacci 表示进行分类,长度为 i 的数有 fib[i-1]个,看做是第 i 组。那么第 i 组的总长度 len[i] = fib[i-1]*i。接下来,看 1 出现的次数。长度为 i 的数的表示中,第 i-1 位肯定是 0。Sum[i] 表 示 前 i 组 的 1 的 个 数 。 可 以 得 到 如 下 式 子 :Sum[i]=sum[i-1]+fib[i-1]+sum[i-2]。第 i 组首位 1 的个数为 fib[i-1],i-1 位为 0,那么最后的 i-2 位的情况,恰好为 1~i-2 组的所有情况。
整体算法也就明了了:
1) 求出 N 位所在的 Fibonacci 表示的数的长度 t
2) 求 1~t 中 Fibonacci 表示中 1 出现的个数。
3) 继续求解剩余字符的 1。
例如求解得到最后对应 Fibonacci 表示为 x=100100
1) 对于长度为 1~5 的 Fibonacci 表示中 1 的个数为 sum[5],i<=100000 中 1
的个数即为 sum[5]+1。
2) 对于 100000<i<=100100,最高位都是 1,共有 fib[3]个,后三位 1 的个数为
sum[2]+1。
3) 1 的总个数为 sum[5]+1+fib[3]+sum[2]+1。$

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,ans,s,i,la,sy;
ll f[105],sum[105],yi[105];
bool ap[105];
void fun(ll x,ll y)
{
	if(x==0)
	{
	  for(i=1;i<=sy;i++)
	   if(ap[i])
	    s++;
	  return;
	}
	x--;
	s+=y;
	for(i=1;i<=72;i++)
	 if(sum[i]>x)
	 {
	   x-=sum[i-1];
	   s+=yi[i-1]+sum[i-1]*y;
	   break;
	 }
	ap[la-i+1]=1;
	fun(x,y+1);
}
int main()
{
	cin>>n;
	f[1]=sum[1]=yi[1]=f[2]=1;sum[2]=yi[2]=2;
	for(i=3;i<=72;i++)
	{
	  f[i]=sum[i-2]+1;
	  sum[i]=sum[i-1]+f[i];
	  yi[i]=yi[i-2]+sum[i-2]+1+yi[i-1];
	}
	for(i=1;i<=72;i++)
	{
	  if(ans+f[i]*i>=n)
	  {
	  	s+=yi[i-1];
		break; 
	  }
	  ans+=f[i]*i;
	}
	n-=ans;
	la=i;sy=n%i;
	ap[1]=1;
	fun(n/i,1);
	cout<<s;
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值