考虑如果没有个数的限制,那么就是一个完全背包,所以先跑一个完全背包,求出没有个数限制的方案数即可。
因为有个数的限制,所以容斥一下:没有1个超过限制的方案=至少0个超过限制-至少1个超过限制+至少2个超过限制-至少3个超过限制+至少4个超过限制
如何求上面的方案数?有限制时,把$c[i]$这个硬币取了超过$d[i]$次是不应该有贡献的,那么我们先取出$d[i]+1$个价值为$c[i]$的硬币,然后剩下的就是$f[sum-c[i]*(d[i]+1)]$,这就是我们所不需要的答案, 把它按容斥的思路搞掉就行了。
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<cmath> #include<cctype> #include<cstdlib> #include<vector> #include<map> #include<set> #define ll long long #define R register int static char B[1<<15],*S=B,*D=B; #define getchar() (S==D&&(D=(S=B)+fread(B,1,1<<15,stdin))?EOF:*S++) using namespace std; inline int g() { R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix; do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; } const int N=100000; int n,tot; int c[5],d[5]; ll f[N+10]; signed main() { for(R i=1;i<=4;++i) c[i]=g(); tot=g(); f[0]=1; for(R i=1;i<=4;++i) for(R j=c[i];j<=N;++j) f[j]+=f[j-c[i]]; while(tot--) { for(R i=1;i<=4;++i) d[i]=g(); register ll sum=g(),ans=0; for(R i=0;i<=15;++i) { R cnt=0; register ll t=sum; for(R j=1;j<=4;++j) if(i&(1<<(j-1))) t-=c[j]*(d[j]+1),cnt^=1; if(t<0) continue; cnt?ans-=f[t]:ans+=f[t]; } printf("%lld\n",ans); } }
2019.06.02