// [7/12/2014 Sjm]
/*
题目:
By the given number n determine the number of its possible different decompositions into Fibonacci sum.
前提:
1) F[86] 是 >=10^18 的最大Fibonacci数;
2)
(由Fibonacci数列的构造式决定,举几个例子就可以知道了)
假设的Fibonacci数的下标区间为[m, n),包括 m, 对n进行拆分,其分解的数目是: (n-m)>>1
假设的Fibonacci数的下标区间为(m, n),不包括 m, 对n进行拆分,其分解的数目是: (n-m-1)>>1
思路:
用 N 减去(与N相等或小于N)的最大的Fibonacci数,并将得到的值付给N,不断循环,直至 N 为零。
(保证拆分出来的Fibonacci数的数目是最少的,使 dp 时不会漏掉情况)
将取得的所有Fibonacci数从小到大排列出来, 存储于 get_index[] (存储的是所取得的Fibonacci的标号)
状态:
dp[i][1]: 在拆分 N 时,已选取 get_index[i] 位置所代表的Fibonacci数, 此情况下不同分解的数目
(言外之意是:不可以将 get_index[i] 位置所代表的Fibonacci数拆了)
dp[i][0]: 在拆分 N 时,没有选取 get_index[i] 位置所代表的Fibonacci数,此情况下不同分解的数目
(言外之意是:可以将 get_index[i] 位置所代表的Fibonacci数用其他Fibonacci数拆了)
分析:
1) dp[i][1]的情况:
由于不可以将 get_index[i] 位置所代表的Fibonacci拆数了,
故决定dp[i][1]的因素是 get_index[i-1] 位置所代表的Fibonacci数
2) dp[i][0]的情况:
此时 get_index[i] 位置所代表的Fibonacci数可拆,
故 在 dp[i-1][0] 的基础上: 需要分解的Fibonacci数下标区间为 [get_index[i-1], get_index[i])
在 dp[i-1][1] 的基础上: 需要分解的Fibonacci数下表区间为 (get_index[i-1], get_index[i])
(求解方法,参见前提)
至于为什么这样分解,可以看一下这样几个例子(关键看从上到下的排列方式):
例1: 13 = 13 例2: 16 = 13 + 3
13 = 8 + 5 16 = 13 + 2 + 1
13 = 8 + 3 + 2 16 = 8 + 5 + 3
16 = 8 + 5 + 2 + 1
决策:
初始状态: dp[0][1] = 1, dp[0][1] = (get_index[i] - 1)>>1;
dp[i][1] = dp[i - 1][1] + dp[i - 1][0];
dp[i][0] = dp[i - 1][0] * ((get_index[i] - get_index[i - 1]) >> 1)
+ dp[i - 1][1] * ((get_index[i] - get_index[i - 1] - 1) >> 1);
*/
1 #include <iostream>
2 #include <cstdlib>
3 #include <cstdio>
4 #include <algorithm>
5 using namespace std;
6
7 typedef __int64 int64;
8 const int MAX = 90;
9
10 int64 F[MAX];
11 int get_index[MAX];
12 int64 dp[MAX][2];
13
14 void Fib()
15 {
16 F[0] = 1; F[1] = 1;
17 for (int i = 2; i < 87; ++i) {
18 F[i] = F[i - 1] + F[i - 2];
19 }
20 }
21
22 int64 Solve(int len)
23 {
24 dp[0][1] = 1;
25 dp[0][0] = (get_index[0] - 1) >> 1;
26 for (int i = 1; i < len; ++i) {
27 dp[i][1] = dp[i - 1][1] + dp[i - 1][0];
28 dp[i][0] =
29 dp[i - 1][0] * ((get_index[i] - get_index[i - 1]) >> 1)
30 + dp[i - 1][1] * ((get_index[i] - get_index[i - 1] - 1) >> 1);
31 }
32 return (dp[len - 1][0] + dp[len - 1][1]);
33 }
34
35 int main()
36 {
37 //freopen("input.txt", "r", stdin);
38 Fib();
39 int T;
40 scanf("%d", &T);
41 while (T--) {
42 int64 n;
43 int len = 0;
44 scanf("%I64d", &n);
45 for (int i = 86; i >= 1; --i) {
46 if (F[i] <= n) {
47 n -= F[i];
48 get_index[len++] = i;
49 }
50 }
51 if (n) {
52 // 若无法将 N 拆分成不等的Fibonacci数之和,输出 0。
53 //(这里不判断也可以AC,但是目前我无法给出“任何一个正整数都可以用不等的Fibonacci数列表示”的数学证明)
54 printf("0\n");
55 continue;
56 }
57 reverse(get_index, get_index + len);
58 printf("%I64d\n", Solve(len));
59 }
60 return 0;
61 }