www 被大佬们|

wscqwq

园龄:2年粉丝:2关注:3

有限背包计数问题

https://class.51nod.com/Html/Textbook/ChapterIndex.html#chapterId=335&textbookId=126

https://class.51nod.com/Html/Challenge/Problem.Html#problemId=1597

如有限背包计数问题

发现对于物品 i 最多填充 ii,而对于 i>n 可以视为完全背包,所以我们分为两类。

对于小的那一类,可以用临时数组维护对应余数的和(像多重背包一样),然后超出范围的再减去。

大的那一类,考虑每次添加一个新的数或者让所有数+1(这样就能涵盖所有的情况,所有数+1相当于偏移,你想让它等于多少,就在最后一个数加入前特定时间加入即可)。

复杂度 O(nn)

#include<iostream>
#include<cmath>
#include<cstring>
#define add(a,b) (a+=b)>=mod&&(a-=mod)
#define sub(a,b) (a-=b)<0&&(a+=mod)
using namespace std;
const int N=100010,mod=23333333,Q=320;
int n,q,tmp[Q],f[2][N],g[2][N];
int main(){
    #ifdef LOCAL
    freopen("1.txt","r",stdin);
    #endif
    #ifndef LOCAL
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    #endif
    cin>>n;
    q=sqrt(n)+1;
    f[0][0]=1;
    for(int i=1;i<q;++i){
        int I=i&1,L=!I;
        memset(tmp,0,i*4);
        int mo=-1;
        for(int j=0;j<=n;++j){
            ++mo;
            if(mo==i)mo=0;
            add(tmp[mo],f[L][j]);
            f[I][j]=tmp[mo];
            if(j>=i*i)sub(tmp[mo],f[L][j-i*i]);//can't reach
        }
    }
    int qq=(q-1)&1;
    // for(int i=1;i<q;++i,puts(""))
    //     for(int j=0;j<=n;++j)
    //         cout<<f[i][j]<<' ';
    g[0][0]=1;
    int ans=f[qq][n],nq=n/q;
    for(int i=1;i<=nq;++i){int I=i&1,L=!I;
        g[I][0]=0;
        for(int j=q;j<=n;++j){
            g[I][j]=g[L][j-q];
            add(g[I][j],g[I][j-i]);
            add(ans,1ll*g[I][j]*f[qq][n-j]%mod);
        }}
    cout<<ans;
    return 0;
}

本文作者:wscqwq

本文链接:https://www.cnblogs.com/wscqwq/p/18315036

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   wscqwq  阅读(7)  评论(0编辑  收藏  举报
历史上的今天:
2023-07-21 BoxInBox
2023-07-21 Make10Again
2023-07-21 Vouchers
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起