题意#
共有 种硬币。面值分别为 。
某人去商店买东西,去了 次,对于每次购买,他带了 枚第 种硬币,想购买价值恰好为 的东西。
多次询问,每次询问形如 ,求付款方法数。
,.
题解#
先不考虑硬币个数限制,用 表示随意凑出 元的方案数,预处理出 .
对每个硬币背包转移,若四种硬币面额为 ,则:
再看硬币个数的限制。
如果只有一种面值为 的硬币,限制最多用 枚,想要凑出 元。
相较于所求, 中多余的部分,就是取出了至少 枚硬币。
根据 的定义可知,取至少 枚硬币后凑出 的方案数为 .
可以理解为:先花掉 枚硬币,剩下随便凑。
所以只有一种硬币时,.
现在就可以开始解决本题中四种硬币的情况了,也是同样的思路,考虑容斥。
直接这样减下去,同时不满足两个硬币的情况会重复,要加回来。
依次类推,同时不满足三个条件的要减下去,同时不满足四个的要加回来。
这个过程可以用二进制枚举方便地解决。
代码#
const int N = 1e5;
int n, c[4], d[4], s, ans;
int f[N + 10]; //f[s]表示:四种面值随便取,取出s的方案数量
signed main() {
cin >> c[0] >> c[1] >> c[2] >> c[3] >> n;
f[0] = 1;
for(int i = 0; i < 4; i++) {
for(int j = c[i]; j <= N; j++) {
f[j] += f[j - c[i]];
}
}
for(int i = 1; i <= n; i++) {
cin >> d[0] >> d[1] >> d[2] >> d[3] >> s;
ans = 0;
for(int sta = 0; sta < (1 << 4); sta++) {
int cnt = 0, sum = 0;
for(int k = 0; k < 4; k++) {
cnt ^= (1 & (sta >> k));
sum += (1 & (sta >> k)) * c[k] * (d[k] + 1);
}
if(s - sum >= 0)
ans += f[s - sum] * (cnt ? -1ll : 1ll);
}
cout << ans << endl;
}
return 0;
}
cpp