2013多校第四场 F题 ZZ’s Fibonacci Problem

题意:

  给一个数字x, x<=1e18, 求其分解成数个fib数相加的方案总数.其中fib数不能重复.

解题思路:

  首先一个结论: 任意一个数,都能被分解成 数个fib数相加的形式.

  另外, 在区间 [1,1e18]总共有 88个fib数.

  对于一个数 x, 我们用一个长度为88的01序列来唯一表示.当前位为1则表示取,否则不取

  例如 8: 00000100...0      2: 00100...0    3: 0110...0  从高位到低位.    

  根据 fib_i =  fib_i-1 + fib_i-2 .

  对于一个序列   001,   其能够被  110 替换. 我们可以总结出. 当前位为1,若其前面有两个0,则可被替换.

2个0替换一个1, 如此连续. 则若 i为1,其后面有cnt个0, 则其方案数为 cnt/2   (计算机整除,2位为1方案)

  另外我们用一个数组来表示状态:

    dp( i, j ) , 表示第 a[i]位为1时, j = 0,表示不取, j = 1表示取的 方案数.

  j = 1 若当前位取, 则只有中方案, 则其可能是由前一位取或者不取而来.所以有:

    dp( i, 1 ) = dp( i-1, 0 ) + dp( i-1, 1 )

  j= 0 若当前位不取, 

    当其为 前一位取而来时, i前面则有  bi - bi-1 个空格, 所以方案数是  ( b_i - b_(i-1) )/2

      dp( i, j ) = dp( i-1, 0 ) * ( ( b_i - b_(i-1) )/2  )  //注意整除.

    当其为 前一位不取而来时,则i前面的0位增加一个,总数为  b_i - b_(i-1) + 1,所以方案数是 (b_i-b_(i-1)+1)/2

      dp( i, j ) = dp( i-1, 1 ) *( (b_i - b_(i-1) + 1) /2 ) 

View Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

typedef unsigned long long LL;
const LL inf = (LL)(1e18);
LL f[100], dp[100][2];

int b[100], cnt;

void init(){
    f[0] = f[1] = 1;
    for(int i = 2; i <= 100; i++){
        f[i] = f[i-1] + f[i-2];
        if( f[i] > inf ){ cnt = i; break; }     
    }
}
int main(){
    init(); 
    int T;
    while(scanf("%d", &T)!=EOF){
    while( T-- ){
        LL x;
        int N = 0;;    
        scanf("%lld", &x);
        for(int k = cnt-1; k >= 0; k--)         
            if( x >= f[k] ) b[N++]=k,x-=f[k];
        reverse( b, b+N );
        dp[0][1] = 1;
        dp[0][0] = (b[0]-1)/2;
        for(int i = 1; i < N; i++){
            dp[i][1] = dp[i-1][0] + dp[i-1][1];
            dp[i][0] = dp[i-1][0]*((b[i]-b[i-1])/2) + dp[i-1][1]*((b[i]-b[i-1]-1)/2);
        }
        printf("%lld\n", dp[N-1][0] + dp[N-1][1] );    
    }
    }    
    return 0;
}

 

posted @ 2013-04-28 18:56  yefeng1627  阅读(139)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor