Luogu P4241 采摘毒瘤 题解
bluewindde · · 题解
特判全部装下的情况。
枚举装不下的最小的物品,则必须装体积小于它的所有物品,其它物品可以随意装,只需要满足最后使枚举的这个物品装不下。
设
将物品按体积从大到小排序,装其它物品的方案可以通过对一段前缀做多重背包得到,答案容易统计。时间复杂度
注意做多重背包时需要提前将当前枚举的物品数量减一,因为装不下意味着该物品还有剩余。
关于单调队列优化多重背包
对于体积为
c 的物品,暴力的多重背包转移是枚举该物品放k 个,从所有可能的k 转移。因为j \bmod c 只有c 种可能,枚举不同的余数r ,将j 表示为j = pc + r ,此时可能转移的p 构成一段左右端点递增的区间,可以用单调队列维护。
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=19260817;
int n,m;
struct node{
int x,c;
friend inline bool operator<(const node &x,const node &y){
return x.c>y.c;
}
}a[505];
int f[505];
int q[100005];
int dp[2][100005];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i)
cin>>a[i].x>>a[i].c;
sort(a+1,a+n+1);
for(int i=n;i;--i)
f[i]=f[i+1]+a[i].x*a[i].c;
if(f[1]<=m){
cout<<1<<endl;
return 0;
}
int ans=0;
dp[0][0]=1;
for(int i=1;i<=n;++i){
for(int r=0;r<a[i].c;++r){
int ql=1,qr=0,s=0;
for(int j=0;j*a[i].c+r<=m;++j){
while(ql<=qr&&j-q[ql]>=a[i].x)
s=(s-dp[(i-1)&1][q[ql++]*a[i].c+r]+mod)%mod;
s=(s+dp[(i-1)&1][j*a[i].c+r])%mod;
q[++qr]=j;
dp[i&1][j*a[i].c+r]=s;
}
}
for(int j=max(m-f[i+1]-a[i].c+1,0);j<=m-f[i+1];++j)
ans=(ans+dp[i&1][j])%mod;
for(int r=0;r<a[i].c;++r){
int ql=1,qr=0,s=0;
for(int j=0;j*a[i].c+r<=m;++j){
while(ql<=qr&&j-q[ql]>a[i].x)
s=(s-dp[(i-1)&1][q[ql++]*a[i].c+r]+mod)%mod;
s=(s+dp[(i-1)&1][j*a[i].c+r])%mod;
q[++qr]=j;
dp[i&1][j*a[i].c+r]=s;
}
}
}
cout<<ans<<endl;
return 0;
}