[loj6089]小 Y 的背包计数问题——背包计数+优化 大佬们的博客 Some Links

题目大意:

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

思路:

体积小于 n 的物品个数很小,所以可以直接多重背包加前缀和优化一下。
体积大于 n 的物品都是永不完的,所以可以直接用完全背包。
裸的完全背包也不可以,观察到物品个数至多 n 个,所以设 g [ i ] [ j ] 表示在 i 个物品体积为 j 的方案数。转移的话这里有一个技巧,即分两种特殊的情况,第一种是 i 个物品的体积全部都大于 n + 1 ,这样的话 g [ i ] [ j ] + = g [ i ] [ j i ] ,可以看成前一种状态所有的物品的体积全部+1,最后还要算上包括了体积为 n + 1 的情况,即 g [ i ] [ j ] + = g [ i 1 ] [ j n 1 ]
代码奇短无比。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
#define DREP(i,a,b) for(int i=a;i>=b;--i)
typedef long long ll;

using namespace std;

void File(){
    freopen("loj6089.in","r",stdin);
    freopen("loj6089.out","w",stdout);
}

const int maxn=1e5+10;
const int maxm=320+10;
const ll mod=23333333;
int n,m;
ll sum[maxn],f[maxn],g[maxm][maxn],ans;

int main(){
    //File();
    scanf("%d",&n);
    m=sqrt(n);
    f[0]=1;
    REP(i,1,m){
        REP(j,0,i-1)sum[j]=f[j];
        REP(j,i,n)sum[j]=(sum[j-i]+f[j])%mod;
        DREP(j,n,i){
            int num=min(i,j/i);
            f[j]=(f[j]+sum[j-i]-((j-(num+1)*i<0) ? 0 : sum[j-(num+1)*i]))%mod;
        }
    }
    REP(i,0,n)g[0][i]=f[i];
    REP(i,1,m)REP(j,m+1,n)
        g[i][j]=(g[i-1][j-m-1]+g[i][j-i])%mod;
    REP(i,0,m)ans=(ans+g[i][n])%mod;
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81448026
今日推荐