[HAOI2008]硬币购物-题解

Posted topsecret

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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

解答

根据容斥原理

[ left|igcap_{i=1}^n overline{S_i} ight| = |U| - left|igcup_{i=1}^n S_i ight| = sum_{0 le kle n}(-1)^ksum_{1le i_1<cdots<i_kle n}left|igcap_{j=1}^k S_{i_j} ight| ]

其中(S_i)表示第(i)种元素超限的取的方法集合,交的初始值是(U)。((U)表全集,(overline A)(A)(U)下的补集)

如何求出(left|igcap_{j=1}^k S_{i_j} ight|)
考虑先求出完全背包的dp值
然后强制将第(i_j)个元素选取超过(d_{i_j})个。
这样的方案总数为(dp[t]-dp[t-sum_{j=1}^k(d_i+1)c_i])。((dp[])的负数项为(0)

然后就可以愉快地容斥了。

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

const int n = 4, mx = 1e5+10, pm[] = {1,-1};
#define int long long

int c[n], d[n], dp[mx] = {1};

signed main() {
    for (int i = 0; i < 4; ++i) {
        cin >> c[i];
        for (int j = c[i]; j < mx; ++j)
            dp[j] += dp[j-c[i]];
    }
    int tot, s;
    cin >> tot;
    while (tot--) {
        for (int i = 0; i < 4; ++i) cin >> d[i];
        cin >> s;
        int res = 0;
        for (int i = 0; i < 16; ++i) {
            int tmp = s, cnt = 0;
            for (int j = 0; j < 4; ++j) {
                if ((i>>j) & 1) {
                    cnt++;
                    tmp -= (d[j]+1)*c[j];
                }
            }
//          cout << cnt << ' ' << tmp << endl;
            res += pm[cnt%2]*(tmp>=0?dp[tmp]:0);
        }
        cout << res << endl;
    }
}

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

HAOI2008硬币购物

[HAOI2008]硬币购物

P1450 [HAOI2008]硬币购物

bzoj 1042: [HAOI2008]硬币购物

bzoj1042[HAOI2008]硬币购物 背包dp+容斥原理

[HAOI2008]硬币购物