hdu 4518 【AC自动机+数位DP+二分】
这个题刚看完题,觉得应该很难!但仔细想想还是应该有思路的(虽然确实挺难的)
题意:
在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的差的绝对值是多少。
思路:至少存在一个通常会转化成一个也不存在。查找一个后缀是否有或者没有一个子串,AC自动机最好。先求出n之前有多少个F数。然后在非波那切数组中查找,到底有多少个最终数记为st。
然后二分的找出第st个和st+1个最终数。
//#pragma comment(linker, "/STACK:102400000") #include<cstdlib> #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<set> #include<map> #include<list> #include<queue> #include<stack> #include<vector> #define tree int o,int l,int r #define lson o<<1,l,mid #define rson o<<1|1,mid+1,r #define lo o<<1 #define ro o<<1|1 #define pb push_back #define mp make_pair #define ULL unsigned long long #define LL long long #define inf 0x7fffffff #define eps 1e-7 #define N 409 #define M 10 using namespace std; int m,n,T,t,cnt; int ch[N][M],v[N],sz; int f[N],last[N]; LL val; char s[30]; LL d[N][20],ans; void init() { sz=1; memset(ch[0],0,sizeof(ch[0])); memset(v,0,sizeof(v)); memset(last,0,sizeof(last)); memset(d,-1,sizeof(d)); } int idx(char c) { return c-'0'; } void insert(char str[],int val) { int u=0; for(int i=0; str[i]; i++) { int c=idx(str[i]); if(!ch[u][c]) { memset(ch[sz],0,sizeof(ch[sz])); ch[u][c]=sz++; } u=ch[u][c]; } v[u]=val; } void getac() { f[0]=0; queue<int>q; for(int c=0; c<M; c++) { int u=ch[0][c]; if(u) { f[u]=0; q.push(u); last[u]=v[u];////////////////// } } while(!q.empty()) { int r=q.front(); q.pop(); for(int c=0; c<M; c++) { int u=ch[r][c]; if(!u) { ch[r][c]=ch[f[r]][c]; } else { q.push(u); int s=f[r]; f[u]=ch[s][c]; last[u]=(v[u]||last[f[u]]);///////////////// } } } } LL fa[100]= {0,1,1}; void ff() { init(); char str[20]; int a=0,b=1,c=2; cnt=2; while(fa[a]+fa[b]<=(LL)1e12) { fa[c]=fa[a]+fa[b]; cout<<"##fa[c]="<<fa[c]<<endl; if(fa[c]>=10) { sprintf(str,"%I64d",fa[c]); insert(str,1); } c=(c+1); a=(a+1); b=(b+1); } cnt=c; getac(); } LL dp(int u,int len,int up) { if(len==0)return 1; if(!up&&d[u][len]!=-1)return d[u][len]; LL ans=0; int end=up?idx(s[n-len]):9; for(int i=0; i<=end; i++) { int c=ch[u][i]; if(last[c]==0) ans+=dp(c,len-1,up&&i==end); } if(!up)d[u][len]=ans; return ans; } LL find(LL x) { LL l=13,r=1e12; while(l<r) { LL mid=(l+r)>>1; sprintf(s,"%I64d",mid); n=strlen(s); if(mid+1-dp(0,n,1)<x) l=mid+1; else r=mid; } return l; } int main() { #ifndef ONLINE_JUDGE freopen("ex.in","r",stdin); #endif ff(); while(scanf("%I64d",&val)==1&&val!=-1) { sprintf(s,"%I64d",val); n=strlen(s); LL num=dp(0,n,1); num=val+1-num; LL x,y; for(int i=1; i<cnt; i++) { if(fa[i]>num) { x=fa[i-1]; y=fa[i]; break; } } LL l=find(x); LL r=find(y); if(fa[1]>num) ans=r-val; else ans=min(val-l,r-val); printf("%I64d\n",ans); } return 0; }