[HAOI2008]硬币购物 题解

[HAOI2008]硬币购物 题解

题目地址:洛谷:【P1450】[HAOI2008]硬币购物 – 洛谷、BZOJ:Problem 1042. — [HAOI2008]硬币购物

题目描述

硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。

输入输出格式

输入格式:
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s

输出格式:
每次的方法数

输入输出样例

输入样例#1:

1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900

输出样例#1:

4
27

说明

di,s<=100000
tot<=1000
[HAOI2008]

题解

每次询问对着跑一遍背包并不现实,所以我们换一种思路。我们预处理不带硬币限制的背包方案数,并且考虑一个容斥的思路。假如不带限制的答案是$dp[s]$,第$i$种硬币限制了$d_i$个,那么从$d_i+1$往后的所有方案都是不可行的,因此要从不带限制的答案中减去一个$dp[s – c_i(d_i+1)]$。这样的话,会减重两个硬币都超了的方案,因此要加上$dp[s – c_i(d_i+1) – c_j(d_j+1)]$,以此类推。
复杂度$O(4 \times 100000 + tot \cdot 2^4)$。

代码

// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 100005;

int c[5], d[5], tot, s, cnt[1 << 4 + 5];
LL dp[MAXN];

int main() {
    for(int i = 1; i < 1 << 4; i++) {
        cnt[i] = cnt[i >> 1] + (i & 1);
    }
    for(int i = 1; i <= 4; i++) {
        c[i] = readint();
    }
    dp[0] = 1;
    for(int i = 1; i <= 4; i++) {
        for(int j = c[i]; j < MAXN; j++) {
            dp[j] += dp[j - c[i]];
        }
    }
    tot = readint();
    while(tot--) {
        for(int i = 1; i <= 4; i++) {
            d[i] = readint();
        }
        s = readint();
        LL ans = dp[s];
        for(int i = 1; i < 1 << 4; i++) {
            int neg = (cnt[i] & 1) ? -1 : 1;
            int res = 0;
            for(int j = 1; j <= 4; j++) {
                if(i & (1 << (j - 1))) {
                    res += c[j] * (d[j] + 1);
                }
            }
            if(s >= res) ans += neg * dp[s - res];
        }
        printf("%lld\n", ans);
    }
    return 0;
}


发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据