题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4518
题意:定义数列F为包含大于10的斐波那契数的数组成的数列,F的前几项为13 ,21, 34, 55, 89,113,121,130,131...定义数列“最终数”为F中的斐波那契项组成的数列,“最终数”的前几项为:13,21,34,89……现在给出n,问“最终数”中与n最接近的数与n的绝对值。
思路:首先将斐波那契额数建立自动机,则给出n我们可以计算小于等于n有多少F数。那么首先计算一下n之前的F数设为t个,在斐波那契中找到t的大致位置x,那么我们要找出哪个数字ans之前的F数有Fib[x]个。二分查找该数。
struct node
{
int next[10],fail,flag;
void init()
{
clr(next,0);
fail=0;
flag=0;
}
void print()
{
int i;
FOR0(i,10) printf("%d ",next[i]);
printf("(%d %d)\n",fail,flag);
}
};
node a[N];
int e;
i64 n;
void insert(int b[],int bNum)
{
int i,k,p=0;
for(i=bNum-1;i>=0;i--)
{
k=b[i];
if(a[p].next[k]==0)
{
a[e].init();
a[p].next[k]=e++;
}
p=a[p].next[k];
}
a[p].flag=1;
}
void insert(i64 x)
{
int b[20],bNum=0;
while(x)
{
b[bNum++]=x%10;
x/=10;
}
insert(b,bNum);
}
queue<int> Q;
void build()
{
int i,j,k,p,q;
FOR0(i,10) if(a[0].next[i]) Q.push(a[0].next[i]);
while(!Q.empty())
{
k=Q.front();
Q.pop();
for(i=0;i<10;i++)
{
if(a[k].next[i])
{
p=a[k].next[i];
q=a[k].fail;
Q.push(p);
a[p].fail=a[q].next[i];
a[p].flag|=a[a[p].fail].flag;
}
else
{
q=a[k].fail;
a[k].next[i]=a[q].next[i];
}
}
}
}
i64 f[20][N];
int b[20],bNum;
i64 F[65];
void init()
{
F[0]=1;
F[1]=2;
int i;
for(i=2;i<=60;i++) F[i]=F[i-1]+F[i-2];
}
i64 DFS(int id,int dep,int flag)
{
if(dep==-1) return 1;
if(!flag&&f[dep][id]!=-1) return f[dep][id];
int i,R=flag?b[dep]:9;
i64 ans=0;
for(i=0;i<=R;i++)
{
if(a[a[id].next[i]].flag) continue;
ans+=DFS(a[id].next[i],dep-1,flag&&i==R);
}
if(!flag) f[dep][id]=ans;
return ans;
}
int cal(i64 x)
{
i64 temp=x;
bNum=0;
while(x)
{
b[bNum++]=x%10;
x/=10;
}
temp=temp+1-DFS(0,bNum-1,1);
return upper_bound(F,F+60,temp)-F;
}
i64 find(i64 x)
{
if(x==0) return -INF;
i64 low=13,high=INF,mid;
while(low<=high)
{
mid=(low+high)>>1;
if(cal(mid)>=x) high=mid-1;
else low=mid+1;
}
if(high>=13&&cal(high)>=x) return high;
return low;
}
int main()
{
init(); clr(f,-1); a[0].init();e=1;
int i;
for(i=5;i<=60;i++) insert(F[i]);
build();
while(scanf("%I64d",&n),n!=-1)
{
i=cal(n);
PR(min(abs(n-find(i)),abs(n-find(i+1))));
}
return 0;
}