P4133 [BJOI2012]最多的方案 二分+DP
作者:互联网
题意:
给定\(n\),将\(n\)用一些斐波那契数的和表示,求有多少种表示方法,使得每种方案里不会出现相同的两个数
范围&性质:\(1\le n\le 10^{18}\)
分析:
由于每一个数只能用至多一次,所以题目就转换成了一个01背包问题,但是这个背包的物品体积和容量过大,所以我们需要考虑优化,首先由于斐波那契数增长极快,第91项时已经超过了\(10^{18}\),所以我们只要先预处理出前91项,之后为了保证方案不重复,我们每次枚举删掉\(n\)范围内最大的斐波那契数,然后用小于ta的所有数去拼成剩下的部分,递归处理,这样可以保证方案不会重复
我们记\(f[i][j]\)表示用前\(i\)个数表示出\(j\)的最大方案数,转移式为\(dp[i][j]=\sum dp[k-1][i-f[k]]\),k的取值的下界就是当\(sum[k]\)(表示前\(k\)个数的和)小于\(i\)是不能选,因为至少保证前\(k\)个数都选时方案存在,所以每次枚举\(k\)时lower_bound求出\(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;
}
标签:契数,方案,sum,BJOI2012,long,斐波,P4133,100,DP 来源: https://www.cnblogs.com/youth518/p/13694585.html