P4133 [BJOI2012]最多的方案 二分+DP

题意:

给定 n n n,将 n n n用一些斐波那契数的和表示,求有多少种表示方法,使得每种方案里不会出现相同的两个数

范围&性质: 1 ≤ n ≤ 1 0 18 1\le n\le 10^{18} 1n1018

分析:

由于每一个数只能用至多一次,所以题目就转换成了一个01背包问题,但是这个背包的物品体积和容量过大,所以我们需要考虑优化,首先由于斐波那契数增长极快,第91项时已经超过了 1 0 18 10^{18} 1018,所以我们只要先预处理出前91项,之后为了保证方案不重复,我们每次枚举删掉 n n n范围内最大的斐波那契数,然后用小于ta的所有数去拼成剩下的部分,递归处理,这样可以保证方案不会重复

我们记 f [ i ] [ j ] f[i][j] f[i][j]表示用前 i i i个数表示出 j j j的最大方案数,转移式为 d p [ i ] [ j ] = ∑ d p [ k − 1 ] [ i − f [ k ] ] dp[i][j]=\sum dp[k-1][i-f[k]] dp[i][j]=dp[k1][if[k]],k的取值的下界就是当 s u m [ k ] sum[k] sum[k](表示前 k k k个数的和)小于 i i i是不能选,因为至少保证前 k k k个数都选时方案存在,所以每次枚举 k k k时lower_bound求出 k k k的下界开始枚举

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	long long f[100],sum[100];
	map<long long,long long> mp[100];
	long long n;
	
	void init()
	{
		f[1]=1;
		f[2]=2;
		for(int i=3;i<=91;i++)
		{
			f[i]=f[i-1]+f[i-2];
		}
		for(int i=1;i<=89;i++)
		{
			sum[i]=sum[i-1]+f[i];
		}
		sum[90]=sum[91]=0x7fffffffffff;
		for(int i=0;i<=91;i++)
		{
			mp[i][0]=1;
		}
	}
	
	void dfs(long long x,int lim)
	{
		if(mp[lim].count(x)) return ;
		int tmp=lower_bound(sum+1,sum+91+1,x)-sum;
		for(int i=tmp;i<=lim;i++)
		{
			if(x-f[i]<0) break;
			dfs(x-f[i],i-1);
			mp[lim][x]+=mp[i-1][x-f[i]];
		}
	}
	
	void work()
	{
		init();
		scanf("%lld",&n);
		dfs(n,91);
		printf("%lld",mp[91][n]);
	}
	
}

 int main()
 {
 	zzc::work();
 	return 0;
 }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值