HDU 4518 吉哥系列故事——最终数(自动机+数位DP)

题目链接: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;
}

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值