10min把解法意淫出来了,然后因为没写过队列优化背包调了1h
做这题时受到了以前做的一题 [HEOI2013] Eden 的新背包问题 的启发。
首先这个题面就很能背包啊,这个“再也装不下剩余的任何一种毒瘤”的限制也挺好转化的,看到数据范围就应该能想到要枚举剩余空间,然后每次跑一遍背包就可以得到 O(n^2m) 的做法。
显然我们要 O(nm) 的做法,这时考虑 Eden 的新背包问题 ,这题告诉我们,一个背包可利用的不应只有遍历完 n 个物品后的数组,回到 \texttt{dp} 的初衷, \texttt{dp} 本质是用子问题解决复杂的母问题,在得到母问题的答案的同时也得到了子问题的答案,而在背包问题中,我们实际上是得到了每一段物品前缀的答案,而在这个题中,再也装不下剩余的任何一种毒瘤就是告诉我们在枚举剩余空间的过程中,将物品从大到小排序后的一段后缀必须被用完,所以实际上只需要对每次前缀的背包求一下答案即可,复杂度 O(nm) 。
#include<bits/stdc++.h>
using namespace std;
#define fio(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
#define tio() freopen("in.txt","r",stdin),freopen("out.txt","w",stdout)
using ll=long long;
const int mod=19260817;
const int N=501,M=1e5+10;
int n,m;
int f[2][M];
int sum;
int ans;
struct dl{int v,num; }d[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d%d",&d[i].num,&d[i].v),sum+=d[i].num*d[i].v;
sort(d+1,d+n+1,[](dl a,dl b)->bool{return a.v>b.v;});
f[0][0]=1;
for(int rs=m,i=1;rs>=0;--rs){
while(i<=n&&d[i].v>rs){
for(int r=0;r<d[i].v;++r){
for(int j=r,s=0;j<=m;j+=d[i].v){
(s+=f[i-1&1][j])%=mod;
if(j/d[i].v>d[i].num)
(s-=f[i-1&1][j-d[i].v*(d[i].num+1)])%=mod;
f[i&1][j]=s;
}
}
sum-=d[i].num*d[i].v;
++i;
}
if(m-rs-sum<0)continue;
(ans+=f[i-1&1][m-rs-sum])%=mod;
}
printf("%d\n",(ans%mod+mod)%mod);
return 0;
}