一眼看去,就是个多重背包嘛~~
但是看一下数据范围,emm,好的,换思路。
首先考虑,如果没有次数限制的话,那么直接完全背包预处理,然后询问就好了。
然后考虑容斥,从中减去不合法的方案。首先肯定会有一种硬币超额使用,对于第种硬币等于说强制选了个,剩下的依然随便选,那么对于第种硬币不合法的方案数等于,于是从里减去就可以了。
另外,第一、二种,第一、三种,第一、四种,第二、三种,第二、四种,第三、四种都非法的方案在上一步中都被减了两次,所以额外都加一次回来。
#include<bits/stdc++.h> #define int long long const int N=1e5+5,INF=0x3f3f3f3f; using namespace std; int n,k,ans,T; int c[5],d[5],f[N]={1}; inline int read() { register int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x*f; } void chu() { for(int i=0;i<4;i++) c[i]=read(); T=read(); for(int i=0;i<4;i++) for(int j=c[i];j<N;j++) f[j]+=f[j-c[i]]; } void solve() { while(T--) { for(int i=0;i<4;i++) d[i]=read(); int s=read(); ans=f[s]; for(int i=1;i<=15;++i) { int now=s; for(int tmp=i,j=k=0;tmp;tmp>>=1,j++) if(tmp&1) k^=1,now-=(d[j]+1)*c[j]; if(now>=0) { if(k) ans-=f[now]; else ans+=f[now]; } } printf("%lld\n",ans); } } signed main() { chu(); solve(); return 0; }