传送门(洛谷)
完全背包和容斥原理的结合
可以看一个区间相减的(其实是错的,但是好理解
求
[
2
,
3
]
[2,3]
[2,3]转换为求
[ 2 , + ∞ ) − ( 3 , + ∞ ) ⇒ [ 2 , 3 ] \begin{aligned} & \;\;[2,+\infty)-(3,+\infty) \\ &\Rightarrow[2,3] \end{aligned} [2,+∞)−(3,+∞)⇒[2,3]
f
[
s
]
f[s]
f[s]表示买
s
s
s的东西有多少种方案(在每种钱都有无限的情况)
f
[
s
]
−
∑
i
=
1
4
f
[
s
−
(
d
[
i
]
+
1
)
∗
c
[
i
]
]
f[s]-\sum_{i=1}^{4}f[s-(d[i]+1)*c[i]]
f[s]−∑i=14f[s−(d[i]+1)∗c[i]]则表示减去这个区间的情况,也就是在钱有限的情况
但是我们不能直接累加起来然后减掉,我们可以看到 f [ s − ( d [ i ] + 1 ) ∗ c [ i ] ] f[s-(d[i]+1)*c[i]] f[s−(d[i]+1)∗c[i]]这是一种钱的情况,因为有可能第1种物品超过限制的同时,第二种物品数量也超过了限制,如果直接累加会把这种情况计算两次,显然是不正确的(即同时两个硬币有限制的情况减了两次)。
注意第一种第二种同时超额、第一种第三种都超额、第一种第四种都超额、第二种第三种都超额、第二种第四种都超额、第三种第四种都超额的方案在上一步中都被减了两次,所以额外都加一次回来
#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);(i)++)
using namespace std;
const int maxn=1e6+10;
const int maxm=1e3+10;
ll s,n;
ll ans;
ll d[maxn],c[maxn],f[maxn];
template <class t> inline void read(t &x) {
int f=1;x=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=10*x+ch-'0';ch=getchar();}
x*=f;
}
void pre_work() {
rep(i,1,4)
rep(j,c[i],100005) {
f[j]+=f[j-c[i]];
}
}
void dfs(ll pos,ll sum,ll flag) {
if(sum<0) return;
if(pos>4) {ans+=(ll)(f[sum]*flag);return;}
dfs(pos+1,sum,flag);
dfs(pos+1,sum-c[pos]*(d[pos]+1),-flag);
}
void readdata() {
f[0]=1;
rep(i,1,4) read(c[i]);
read(n);
pre_work();
rep(i,1,n) {
rep(i,1,4) read(d[i]);
read(s);
ans=0;
dfs(1,s,1);
printf("%lld\n",ans);
}
}
int main() {
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
readdata();
//work();
return 0;
}