hdu题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4099
题意:给出一个不超过40长度的前缀,在前10W项fib项数中查找最小满足前缀的项数。
思路:因为fib数列增长很快,而且查询长度不超过40,我们可以考虑用高精度计算然后用字典树维护每一项的前40位。
大概计算55位长度就够了(因为取前40位,后面造成的误差不影响前40位的计算),因为每次加的时候最多进一位,所以进位的时候都要向后移,保证加法的时候数位对齐。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <utility>
using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))
#define LL long long
#define ULL unsigned long long
//**************trie
struct trie
{
trie *next[10];
int last;
};
trie *root;
void init(trie *x)
{
rep(i,0,9) x->next[i] = NULL;
x->last = 111111;
}
void insert(trie *x,int a[],int pos,int k)
{
int num = 0;
while( pos && num<40 )
{
if ( x->next[a[pos]] == NULL )
{
x->next[a[pos]] = new trie;
init(x->next[a[pos]]);
x->next[a[pos]]->last = k;
}
x = x->next[a[pos]];
pos--;
num++;
}
}
int query(char s[])
{
trie *p = root;
int len = strlen(s);
rep(i,0,len-1)
{
int index = s[i] - '0';
if ( p->next[index] == NULL ) return -1;
p = p->next[index];
}
if ( p->last == 111111 ) return -1;
return p->last;
}
//**************trie
void Init()
{
root = new trie;
init(root);
int a[4][100];
Clean(a,0);
a[0][0] = a[0][1] = 1;
a[1][0] = a[1][1] = 1;
insert(root,a[0],1,0);
int k1,k2,k3;
int k,l;
rep(j,2,99999)
{
k = 0;
l = 55;
k1 = j % 3;
k2 = ( k1 + 1 ) % 3;
k3 = ( k2 + 1 ) % 3;
Clean(a[3],0);
Clean(a[k1],0);
rep(i,1,55)
{
a[3][i] += a[k2][i] + a[k3][i];
if ( a[3][i] >= 10 )
{
a[3][i] -= 10;
a[3][i+1]++;
}
}
if ( a[3][56] )
{
k = 1;
rep(i,1,a[k2][0]) a[k2][i] = a[k2][i+1];
rep(i,1,a[k3][0]) a[k3][i] = a[k3][i+1];
}
rep(i,1,55) a[k1][i] = a[3][i+k];
while( !a[k1][l] ) l--;
a[k1][0] = l;
insert(root,a[k1],l,j);
}
}
int main()
{
Init();
char t[50];
int T;
cin>>T;
getchar();
rep(i,1,T)
{
gets(t);
printf("Case #%d: %d\n",i,query(t));
}
return 0;
}