P1450 [HAOI2008] 硬币购物

P1450 硬币购物

DP做法显然大家都能想到,但是一看多组数据,凉了。

我一看\(s \le 10^5\) 显然可以预处理出来无限硬币可组成的情况。

那末,我们可以使用容斥做这题(反正只有4种)。

答案看程序。

关于为什么是 \(ans - f_{s-c_{i}\cdot (d_{i}+1)}\) 笔者也想了很久。

愚以为减掉限定第 \(i\) 种硬币使用 \(d_{i}+1\) 枚也就直接减掉了 \(d_{i}+n\) 枚的情况 \((n \in N^{+})\) 。因为 \(f_{s-c_{i} \cdot (d_{i} + 1)}\)(限定使用 \(d_{i} + 1\) 枚) 是由 \(f_{s - c_{i} \cdot (d_{i}+2)}\) (限定使用 \(d_{i}+2\)枚) 转移过来的。

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

typedef long long ll;
const ll MAXN = 5e5+10;

ll N, c[10], d[10], f[MAXN];

int main() {
    for (ll i = 0; i < 4; i++) {
        scanf("%lld", c+i);
    }
    scanf("%lld", &N);
    f[0] = 1;
    for (ll i = 0; i < 4; i++) {
        for (ll j = c[i]; j <= MAXN - 10; j++) {
            f[j] += f[j-c[i]];
        }
    }
    for (ll i = 1; i <= N; i++) {
        for (ll i = 0; i < 4; i++) {
            scanf("%lld", d+i);
        }
        ll s;
        scanf("%lld", &s);
        ll ans = f[s];
        for (ll i = 1; i < (1LL << 4); i++) {
            ll tmp = 0, tim = 0;
            for (ll j = 0; j < 4; j++) {
                if (i & (1LL << j)) {
                    tmp += c[j] * (d[j] + 1);
                    tim++;
                }
            }
            if (s < tmp) continue;
            else {
                if (tim & 1) ans -= f[s - tmp];
                else ans += f[s - tmp];
            }
        }
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2020-09-13 21:26  Gensokyo_Alice  阅读(90)  评论(0编辑  收藏  举报