题目描述
题目解析
100
分
做
法
:
D
P
100 分做法:DP
100分做法:DP
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;
}