[dp][uestc][详细]L - 菲波拉契数制升级版

L - 菲波拉契数制升级版

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)

Submit Status

我们定义如下数列为菲波拉契数列:

F(1)=1

F(2)=2

F(i)=F(i−1)+F(i−2)(i>=3)

给定任意一个数,我们可以把它表示成若干互不相同的菲波拉契数之和。比如13有三种表示法

13=13

13=5+8

13=2+3+8

现在给你一个数n,请输出把它表示成若干互不相同的菲波拉契数之和有多少种表示法。

Input

第一样一个数T,表示数据组数,之后T行,每行一个数n。

T≤105

1≤n≤10^18

Output

输出T行,每行一个数,即n有多少种表示法。

数据很大,数组开不下。

先定序地考虑一个菲波拉契数如fib(i)的表示法,假设i比较大,由菲波拉契数的定义可知道fib(i)=fib(i-1)+fib(i-2);要找到其它表示就继续拆分fib(i-1)或fib(i-2),假如拆分fib(i-1)得到fib(i)=2*fib(i-2)+fib(i-3),这不能保证保证互不相同的性质(补充,也许有人会考虑将这个式作为中间过程通过拆出矛盾项来保证唯一性,然而消除了一个矛盾必然产生新的矛盾,所以不行,具体地说将fib(i-2)=fib(i-3)+fib(i-4)消除了fib(i-2)的矛盾然而产生了fib(i-3)的矛盾,fib(i-3)又是由fib(i-1)得来,无法通过有限次拆分将矛盾消除),因此考虑拆fib(i-2),又得到一种表达方式fib(i)=fib(i-1)+fib(i-3)+fib(i-4);继续拆分,和前面类似,只能拆fib(i-4);不难发现每次只能拆最小的fib数,终点为fib(1)或fib(2);由上面过程可以得出:1或2+2*m=i,m是除了fib(i)本身以外其他的表示方法,上式可以得出m=(i-1)/2(/都表示向下取整,下同)。
ok,我们成功地解决了n为菲波拉契数的情况;下面考虑n为菲波拉契数的组合的情况;假设n=fib(i)+fib(j),i>j;我们知道fib(i)和fib(j)单独的情况,现在组合起来可以借鉴刚刚拆分的思想,不同的是现在可能fib(i)和fib(j)都可以拆分;假设拆分fib(j)(可能你要问,为什么要拆小的那个?原因是在fib(i)拆分中会有和fib(j)拆分冲突的情况)得到(j-1)/2+1组;现在考虑拆fib(i),和一开始一样的思想只是终点发生了变化,为确定终点(保证独立性),我们考察fib(j)的拆分情况,我们发现在(j-1)/2种拆分中都是以fib(j-1)开头的,因此终点为fib(j)或fib(j+1),可以得到等式2*m1+j或(j+1)=i,m1=(i-j)/2,m1是除了fib(i)本身以外其他的表示方法;以fib(j)开头,即fib(j)本身,终点为fib(j+1)或fib(j+2),m2=(i-j-1)/2,m2是除了fib(i)本身以外其他的表示方法。综上,含fib(i)的表示有(j-1)/2+1种,不含fib(i)的表示有m1*(j-1)/2+m2。现在推广到n=fib(i)+fib(j)+fib(k),i>j>k的情况fib(j)和fib(k)的组合已经知道了,而且分两组以fib(j)开头的和以fib(j-1)开头的,对fib(i)拆分和上一种办法一样,由此给出dp[i][0]的定义:以fib(i-1)开头的所有组合,dp[i][1]:以fib(i)开头的所有组合。于是有状态转移方程dp[i][1]=dp[j][0]+dp[j][1],dp[i][0]=(i-j-1)/2*dp[j][1]+(i-j)/2*dp[j][0],最终结果为dp[maxi][0]+dp[maxi][1]。

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

long long int dp[88][2];//dp[i][0] 表示comb[0~i]的所有组合的降序排列中以fib[i-1]开头的 [1]表示以fib[i]开头的
long long int fib[88];

void get_fibs()
{
fib[0]=1;fib[1]=1;
for(int i=2;i<88;i++)
fib[i]=fib[i-1]+fib[i-2];
}
vector<int> comb;//组成n的fib的下标
long long int n;
void solve()
{
comb.clear();
for(int i=87;i>0&&n;i--)
if(n>=fib[i])
{
n-=fib[i];
comb.push_back(i-1);
}
sort(comb.begin(),comb.end());
dp[0][1]=1;
dp[0][0]=comb[0]/2;
for(int i=1;i<comb.size();i++)
{
dp[i][1]=dp[i-1][0]+dp[i-1][1];
dp[i][0]=(comb[i]-comb[i-1]-1)/2*dp[i-1][1]+(comb[i]-comb[i-1])/2*dp[i-1][0];
}
printf("%lld\n",dp[comb.size()-1][1]+dp[comb.size()-1][0]);
}

int main()
{
get_fibs();
int T;
scanf("%d",&T);
while(T--)
{
scanf("%lld",&n);
solve();
}

}

时间: 2024-08-02 06:57:25

[dp][uestc][详细]L - 菲波拉契数制升级版的相关文章

UESTC_菲波拉契数制升级版 2015 UESTC Training for Dynamic Programming&lt;Problem L&gt;

L - 菲波拉契数制升级版 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 我们定义如下数列为菲波拉契数列: F(1)=1 F(2)=2 F(i)=F(i−1)+F(i−2)(i>=3) 给定任意一个数,我们可以把它表示成若干互不相同的菲波拉契数之和.比如13有三种表示法 13=13 13=5+8 13=2+3+8 现在给你一个数n,请输出把它表示成

UESTC_菲波拉契数制 2015 UESTC Training for Dynamic Programming&lt;Problem E&gt;

E - 菲波拉契数制 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 我们定义如下数列为菲波拉契数列: F(1)=1 F(2)=2 F(i)=F(i−1)+F(i−2)(i>=3) 给定任意一个数,我们可以把它表示成若干互不相同的菲波拉契数之和.比如13有三种表示法 13=13 13=5+8 13=2+3+8 现在给你一个数n,请输出把它表示成若干互

CDOJ 1133 菲波拉契数制 变直接统计为构造

菲波拉契数制 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status 我们定义如下数列为菲波拉契数列: F(1)=1F(1)=1 F(2)=2F(2)=2 F(i)=F(i−1)+F(i−2)(i>=3)F(i)=F(i−1)+F(i−2)(i>=3) 给定任意一个数,我们可以把它表示成若干互不相同的菲波拉契数之和.比如1313有三种表示法 13=1313

菲波拉契数列(传统兔子问题)

题目: 古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 斐波那契数: 亦称之为斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列.费波那西数列.费波拿契数.费氏数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.……在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字来说,就

C语言-郝斌笔记-005菲波拉契序列

菲波拉契序列 1 /* 2 菲波拉契序列 3 1 2 3 5 8 13 21 34 4 */ 5 6 # include <stdio.h> 7 8 int main(void) 9 { 10 int n; 11 int f1, f2, f3; 12 int i; 13 14 f1 = 1; 15 f2 = 2; 16 17 printf("请输入您需要求的想的序列: "); 18 scanf("%d", &n); 19 20 if (1 ==

菲波拉契数列

1 /* 2 * 古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子, 3 * 假如兔子都不死,问每个月的兔子总数为多少? 4 //这是一个菲波拉契数列问题 5 6 */ 7 public class Test7 { 8 public static void main(String[] args) { 9 System.out.println("第1个月:" + 1); 10 System.out.println("第2个月:&

用递归求菲波拉契序列第N项的值

1 #include <stdio.h> 2 /* 3 题目:用递归求菲波拉契序列第N项的值 4 */ 5 int func(int n); 6 7 int main(void) 8 { 9 int N; 10 gogogo: printf("输入要求的项数(例:求第3项的值输入3)\n"); 11 scanf("%d",&N); 12 printf("第%d项的值 = %d\n",N,func(N)); 13 14 goto

7,菲波拉契数

题目一: 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)n<=39 public int Fibonacci(int n) { if(n==0) return 0; if(n==1) return 1; return Fibonacci(n-1)+Fibonacci(n-2); } 题目二: 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果). n个台阶:第一次跳一阶,还剩n-

求菲波拉契数列第n项的值

public int Solution(int n) { if(n < 3) { return 1; }else { int[] arr = new int[n]; arr[0] = 1; arr[1] = 1; for(int i = 3;i <= n;i++) { arr[i] = arr[i-1] + arr[i-2]; } return arr[n]; } }