bzoj 1042 [HAOI2008]硬币购物 容斥

Posted copperoxide

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 1042 [HAOI2008]硬币购物 容斥相关的知识,希望对你有一定的参考价值。

题面

题目传送门

解法

挺妙的一道题

显然不能每一次都多重背包,效率太低

发现物品种类只有4种,可以干点什么

(f_i)表示所有物品都无限取的时候的方案数

这个可以通过无限背包处理出来

对于每一次询问,答案即为得到面值S的超过限制的方案数 – 第1种硬币超过限制的方案数 – 第2种硬币超过限制的方案数 – 第3种硬币超过限制的方案数 – 第4种硬币超过限制的方案数 + 第1,2种硬币同时超过限制的方案数 + 第1,3种硬币同时超过限制的方案数 + …… + 第1,2,3,4种硬币全部同时超过限制的方案数

假设第一种超过了限制,那么方案数为(f_{i-c_i×(d_i+1)})

时间复杂度:(O(s+2^4×T))

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == ‘-‘) f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - ‘0‘, c = getchar(); x *= f;
}
int c[4], d[4], f[100010];
main() {
    for (int i = 0; i < 4; i++) read(c[i]); f[0] = 1;
    for (int i = 0; i < 4; i++)
        for (int j = c[i]; j <= 1e5; j++)
            f[j] += f[j - c[i]];
    int T; read(T);
    while (T--) {
        for (int i = 0; i < 4; i++) read(d[i]);
        int s; read(s); int ans = 0;
        for (int i = 0; i <= 15; i++) {
            int now = s, k = 1;
            for (int j = 0; j < 4; j++)
                if ((i >> j) & 1) now -= c[j] * (d[j] + 1), k ^= 1;
            if (now < 0) continue;
            if (k) ans += f[now]; else ans -= f[now];
        }
        cout << ans << "
";
    }
    return 0;
}

以上是关于bzoj 1042 [HAOI2008]硬币购物 容斥的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ1042[HAOI2008]硬币购物 容斥

bzoj 1042: [HAOI2008]硬币购物 dp+容斥原理

bzoj 1042: [HAOI2008]硬币购物

BZOJ-1042: [HAOI2008]硬币购物 (背包DP+容斥原理)

bzoj1042 HAOI2008—硬币购物

[bzoj1042] [HAOI2008]硬币购物