题目意思

你来到一个商店,你带了c1,c2,c3,c4四种面值的硬币,以及购买次数tot。
接下来tot行,每行有d1,d2,d3,d4分别对应四种硬币的使用个数上限,以及你想要购买的物品价值数s。
询问你有几种合理的方法使用合理的硬币数量购买到价值s的物品?

Solution

正面?处理不来,情况太多太复杂,或者说对着4个物品直接来tot次多重背包,复杂度炸上天。
那么反面?假设硬币不存在上限的话,很基础的完全背包了。
那么回到要求,我们有4种硬币存在数量限制,那么是不是可以用容斥转换呢?当然是可以的。
那么答案就转换为了不存在数量限制-1,2,3,4存在限制的情况。这样又减多了,要把某两个存在限制的加回来,再减掉三个存在限制,再加上四个存在限制。
我们只要假设存在限制就是第i种硬币多花一个,用S-sum就是得到的剩下钱随便花得到的方案数。
接下来码代码就行了。

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 7;

int c[5], d[5];
long long dp[N];
int tot, s;

int main() {
    dp[0] = 1;
    scanf("%d %d %d %d %d", &c[1], &c[2], &c[3], &c[4], &tot);
    for (int i = 1; i <= 4; ++i)
        for (int j = c[i]; j < N; ++j)
            dp[j] += dp[j - c[i]];
    // 以上为完全背包

    while (tot--) {
        scanf("%d %d %d %d %d", &d[1], &d[2], &d[3], &d[4], &s);
        long long ans = 0;
        for (int i = 0; i < 1 << 4; ++i) {
            long long tmp = 0;
            for (int j = 0; j < 4; ++j) {
                if (i & (1 << j))
                    tmp += 1ll * c[j + 1] * (d[j + 1] + 1);    //多花一个硬币的金钱
            }
            int op = 1;
            int cnt = __builtin_popcount(i); //记录二进制表示i的情况下有几个1
            if (cnt & 1)    op = -1;
            if (s >= tmp)    ans += op * dp[s - tmp];
        }
        printf("%lld\n", ans);
    }
    return 0;
}