HDU 4518 ac自动机+数位dp

吉哥系列故事——最终数

Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 304    Accepted Submission(s): 102


Problem Description
  在2012年腾讯编程马拉松比赛中,吉哥解决了一道关于斐波那契的题目,这让他非常高兴,也更加燃起了它对数学特别是斐波那契数的热爱。现在,它又在思考一个关于斐波那契的问题:
  假如我们现在已知斐波那契数是1,1,2,3,5,8,13,21,34,55,89...
  由于吉哥特别喜欢斐波那契数,它希望每个数中都包含斐波那契数,比如说130,其中包含了13,或者5534包含了55和34,只要数位中含有至少一个斐波那契数就是吉哥想要的数。但是这种数实在太多了,于是它去掉那些仅仅含有小于10的斐波那契数的数,比如说1,仅仅含有1,所以被去掉;或者335只含有3和5,都是小于10的斐波那契数,所以也去掉;但是131是留下的,因为它含有13,我们暂且称这类数为F数,不难得到前几个F数是 13 ,21, 34, 55, 89,113,121,130,131...
  霸气的吉哥觉得这样还不够,它想将斐波那契进行到底——在前面F数的基础上,吉哥要得到那些是第斐波那契数个的F数!就是说,我们假设F数从1开始标号,那么13是第1个F数,吉哥想要那些在F数中的排列或者说下标也要是斐波那契数的数,吉哥称之为最终数,如13,21,34,89,130...
  现在给你一个数n,吉哥想知道离n最近的最终数与n的差的绝对值是多少。
 

Input
  输入包含多组测试数据。
  每组测试数据包含一个整数n ( 1 <= n <= 10^11);
  若n = -1,表示输入结束。
 

Output
  对于每个n,请输出离n最近的最终数与n的差的绝对值。
 

Sample Input
  
  
1 100 -1
 

Sample Output
  
  
12 11
 

Source
 


把斐波那契数列插入ac自动机,然后数位dp进行二分查找,

代码:

/* ***********************************************
Author :rabbit
Created Time :2014/8/15 16:27:37
File Name :3.cpp
************************************************ */
#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <string>
#include <time.h>
#include <math.h>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define eps 1e-8
#define pi acos(-1.0)
typedef long long ll;
struct Trie{
	ll next[1010][10],end[1010],fail[1010];
	ll root,L;
	void init(){
		L=0;
		root=newnode();
	}
	ll newnode(){
		for(ll i=0;i<10;i++)
			next[L][i]=-1;
		end[L++]=0;
		return L-1;
	}
	void insert(ll x){
		ll a[15],len=0;
		while(x){
			a[len++]=x%10;
			x/=10;
		}
		//for(ll i=len-1;i>=0;i--)cout<<a[i]<<" ";cout<<endl;
		ll now=root;
		for(ll i=len-1;i>=0;i--){
			if(next[now][a[i]]==-1)
				next[now][a[i]]=newnode();
			now=next[now][a[i]];
		}
		end[now]=1;
	}
	void build(){
		queue<ll> Q;
		fail[root]=root;
		for(ll i=0;i<10;i++)
			if(next[root][i]==-1)
				next[root][i]=root;
			else{
				fail[next[root][i]]=root;
				Q.push(next[root][i]);
			}
		while(!Q.empty()){
			ll now=Q.front();
			Q.pop();
			end[now]|=end[fail[now]];
			for(ll i=0;i<10;i++)
				if(next[now][i]==-1)
					next[now][i]=next[fail[now]][i];
				else{
					fail[next[now][i]]=next[fail[now]][i];
					Q.push(next[now][i]);
				}
		}
	}
}ac;
ll fib[60],dp[15][1010],seq[60],POW[20];
ll suf[15],num[15];
ll dfs(ll pos,ll st,bool flag){
	if(pos==0)return 0;
	if(flag&&dp[pos][st]!=-1)return dp[pos][st];
	ll u=flag?9:num[pos];
	ll ans=0;
	for(ll i=0;i<=u;i++){
		ll state=ac.next[st][i];
		if(ac.end[state]){
			if(flag||i<u)ans+=POW[pos-1];//后面的任意取
			else ans+=suf[pos-1]+1;
		}
		else ans+=dfs(pos-1,state,flag||i<u);
	}
	if(flag)dp[pos][st]=ans;
	return ans;
}
ll cal(ll x){
	ll len=0;
	suf[0]=0;
	//cout<<"x="<<x<<endl;
	while(x){
		num[++len]=x%10;
		suf[len]=suf[len-1]+x%10*POW[len-1];
		x/=10;
	}
	//cout<<"suf: ";for(ll i=1;i<=len;i++)cout<<suf[i]<<" ";cout<<endl;
	return dfs(len,ac.root,0);
}
int main()
{
     //freopen("data.in","r",stdin);
     //freopen("data1.out","w",stdout);
     fib[1]=1;
	 fib[2]=1;
	 for(ll i=3;i<=60;i++)
		 fib[i]=fib[i-1]+fib[i-2];
	 memset(dp,-1,sizeof(dp));
	 ac.init();
	 for(ll i=1;i<=56;i++)
		 if(fib[i]>10)ac.insert(fib[i]);
	 POW[0]=1;
	 for(ll i=1;i<=15;i++)POW[i]=10*POW[i-1];
	 ac.build();
     for(ll i=2;i<=56;i++){
		 ll l=13,r=POW[12];
		 while(l<r){
			 ll mid=(l+r)/2;
			 ll ret=cal(mid);
			// cout<<"han "<<i<<" "<<l<<" "<<r<<" "<<ret<<endl;
			 if(ret<fib[i])l=mid+1;
			 else r=mid;
		 }
		 seq[i]=l;
	 }
		//for(ll i=2;i<=56;i++)cout<<seq[i]<<endl;return 0;
	   	ll n;
		while(cin>>n&&n!=-1){
			ll ans=1e15;
			 for(ll i=2;i<=56;i++){
				 ll tt=n-seq[i];
				 if(tt<0)tt=-tt;
				 ans=min(ans,tt);
			 }
			 cout<<ans<<endl;
		 }
		 
     return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值