BZOJ.2660.[BJOI2012]最多的方案(DP)

题目链接

首先我们知道:
1
也很好理解。如果相邻两项出现在斐波那契表示法中,那它们显然可以合并。
所以我们能得到\(n\)的斐波那契表示,记\(pos[i]\)\(n\)的斐波那契表示法中,第\(i\)项在原斐波那契的下标,那么:\(n=\sum_{i=1}^{cnt}F[pos[i]]\)
如果方案中不直接存在\(F[pos[i]]\)(把\(F[pos[i]]\)分解),那它只能由\(<pos[i]\)的项构成。于是我们考虑递推。
\(f[i][1/0]\)表示当前考虑到\(pos[i]\),是/否分解\(F[pos[i]]\),且满足\(F[pos[1]]\sim F[pos[i]]\)都被组成,的方案数。

如果不分解\(F[pos[i]]\),那么有\[f[i][0]=f[i-1][0]+f[i-1][1]\],且\(F[pos[i+1]]\)只能由\([pos[i]+1,pos[i+1]]\)之间的项得到。

如果分解\(F[pos[i]]\),则\(F[pos[i+1]]\)可以由\([pos[i],pos[i+1]]\)之间的项得到,而且若分解\(F_i=F_{i-1}+F_{i-2}\),下一次只能分解\(F_{i-2}=F_{i-3}+F_{i-4}\),再下一次只能分解\(F_{i-4}=\ldots\ldots\)。于是我们可以得到在区间\([l,r-1]\)中分解\(F_r\)的方案数为\(\frac{r-l}{2}\)
于是可以得到:\[f[i][1]=f[i-1][0]\times\frac{pos_i-pos_{i-1}-1}{2}+f[i-1][1]\times\frac{pos_i-pos_{i-1}}{2}\]

初始值就是\(f[1][0]=1,f[1][1]=\frac{pos_i-1}{2}\)

另外直接map记搜也能跑得飞快(甚至比递推快smg。。)

#include <cstdio>
#include <algorithm>
typedef long long LL;
const int N=100;

int p[N],f[N][2];
LL n,F[N];

int main()
{
    scanf("%lld",&n);
    F[1]=1, F[2]=2; int t,cnt=0;
    for(t=3; (F[t]=F[t-1]+F[t-2])<=n; ++t);
    for(int i=t; i; --i)
        if(n>=F[i]) n-=F[i], p[++cnt]=i;
    std::reverse(p+1,p+1+cnt);

    f[1][0]=1, f[1][1]=p[1]-1>>1;
    for(int i=2; i<=cnt; ++i)
        f[i][0]=f[i-1][0]+f[i-1][1],
        f[i][1]=f[i-1][0]*(p[i]-p[i-1]-1>>1)+f[i-1][1]*(p[i]-p[i-1]>>1);
    printf("%d\n",f[cnt][0]+f[cnt][1]);

    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/9667017.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值