[loj6089]小Y的背包计数问题

https://www.zybuluo.com/ysner/note/1285358

题面

\(Y\)有一个大小为\(n\)的背包,并且小\(Y\)\(n\)种物品。
对于第\(i\)种物品,共有\(i\)个可以使用,并且对于每一个\(i\)物品,体积均为\(i\)
求小\(Y\)把该背包装满的方案数为多少,答案对于\(23333333\)取模。
定义两种不同的方案为:当且仅当至少存在一种物品的使用数量不同。

  • \(n\leq10^5\)

    解析

    这个背包问题让我耳目一新啊。
    \(idea\)棒棒的。

注意到题目中物品\(i\)\(i\geq\sqrt n\))的个数限制实际上是不存在的。
所以可以把这个问题分为两个子问题:多重背包问题和完全背包问题。

\(f[i][v]\)表示前\(i\)个物品,总体积为\(v\)时的方案数。
对于\(i\leq\sqrt n\):(多重背包问题)
很显然有\[f[i][v]=\sum_{j=1}^if[i-1][v-j*i]\]
可以前缀和优化做到\(O(n\sqrt n)\)

对于\(i\geq\sqrt n\):(完全背包问题)
又注意到一个物品最多取\(\sqrt n\)个。
同样设个\(g[i][v]\)表示方案数。
可以认为我们要\(DP\)出一个和为\(n\),最小数至少\(\sqrt n+1\)的不下降序列
(序列中的数是物品体积)。
转移有两种:

  • 在序列开头加入一个数\(\sqrt n+1\)
  • 把序列中所有数\(+1\)

\[g[i][v]=g[i-1][v-\sqrt n-1]+g[i][v-i]\]
这个复杂度\(O(n)\)
最后讨论一下给前一个问题分配多少体积,后一个问题分配多少体积,统计答案即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define re register
#define il inline
#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=2005,inf=2e9,mod=23333333;
int n,f[N],g[350][N],s[N],ans=-inf,m;
il ll gi()
{
  re ll x=0,t=1;
  re char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
int main()
{
  n=gi();m=sqrt(n);
  f[0]=1;
  fp(i,1,m)
    {
      fp(j,0,i) s[j]=f[j];
      fp(j,i,n) s[j]=(f[j]+s[j-i])%mod;//前缀和
      fp(j,0,n)
    {
      f[j]=s[j];
      if(j-i*(i+1)>=0) f[j]=(f[j]-s[j-i*(i+1)]+mod)%mod;//去掉不合法状态
    }
    }
  g[0][0]=1;ans=f[n];
  fp(i,1,m)
    for(re int j=i*(m+1);j<=n;j++)
      {
    g[i][j]=(g[i-1][j-m-1]+g[i][j-i])%mod;
    (ans+=1ll*g[i][j]*f[n-j]%mod)%=mod;
      }
  printf("%d\n",ans);
  return 0;
}

转载于:https://www.cnblogs.com/yanshannan/p/9668700.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值