1. 程式人生 > >[51nod1597] 有限揹包計數問題

[51nod1597] 有限揹包計數問題

題目大意

你有一個大小為n的揹包,你有n種物品,第i種物品的大小為i,且有i個,求裝滿這個揹包的方案數有多少
兩種方案不同當且僅當存在至少一個數i滿足第i種物品使用的數量不同

n≤100000,答案模23333333,時限2.333s

分析

這道題一看是一道多重揹包,但是範圍有點大啊。。。
可以嘗試利用一下題目的條件,對於in,就做一次多重揹包。合併一種物品時,通過字首和可以優化到O(n)。
對於i>n,可以當成完全揹包來做!
但是直接上完全揹包又很慢。。。
但是可以注意到另一點:這些物品使用個數也不超過n,那麼一個思路出來了:設g[i][j]表示用i個物品,體積為j的方案數。
轉移:g

[i][j]=g[i][ji]+g[i1][jn1]。第一個表示把當前i個物品的體積都加1,第二個表示加入一個體積為n+1的物品。由於加入物品的體積是不上升的,所以是正確的。
時間複雜度O(nn)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=100005,mo=23333333;

typedef long long LL;

int n,g[2][N],p,q,m,f[2][N],s,t,ans;

int
main() { scanf("%d",&n); for (m=n;m>n/m;m--); p=0; q=1; f[0][0]=1; for (int i=1;i<=m;i++,p^=1,q^=1) { memset(f[q],0,sizeof(f[q])); for (int j=0;j<i;j++) { s=0; for (int k=0;k*i+j<=n;k++) { s=(s+f[p][k*i+j])%mo; f[q][k*i+j]=s; if
(k>=i) s=(s+mo-f[p][(k-i)*i+j])%mo; } } } t=p; ans+=f[t][n]; g[0][0]=1; p=0; q=1; for (int i=1;i<=m;i++,p^=1,q^=1) { memset(g[q],0,sizeof(g[q])); for (int j=m+1;j<=n;j++) { g[q][j]=g[p][j-m-1]; if (j>=i) g[q][j]=(g[q][j]+g[q][j-i])%mo; } for (int j=0;j<=n;j++) ans=(ans+(LL)f[t][j]*g[q][n-j])%mo; } printf("%d\n",ans); return 0; }