洛谷P1450 [HAOI2008] 硬币购物 容斥/背包计数

对于第 \(i\) 种金币,用 \(1\) 表示第 \(1\) 种金币的数量不合法,用 \(0\) 表示第 \(i\) 种金币数量合法,比如:\(0000\) 表示的就是 \(4\) 种金币数量都合法的情况数(这也是我们最终要求的部分,总方案数减去这部分就是答案),而 \(0100\) 表示的是第 \(2\) 种金币数量不合法(大于了 \(d_i\) 枚),其它都合法的情况数。

然后我们按照

\(|A_1 \lor A_2 \lor A_3 \lor A_4|\)

\((|A_1 + A_2 + A_3 + A_4|)\)

\(- (|A_1 \land A_2| + |A_1 \land A_3| + |A_1 \land A_4| + |A_2 \land A_3| + |A_2 \land A_4| + |A_3 \land A_4|)\)

\(+ (|A_2 \land A_3 \land A_4| + |A_1 \land A_3 \land A_4| + |A_1 \land A_2 \land A_4| + |A_1 \land A_2 \land A_3|)\)

\(- |A_1 \land A_2 \land A_3 \land A_4|\)

的顺序来推导:

顺序 1111 1110 1101 1100 1011 1010 1001 1000 0111 0110 0101 0100 0011 0010 0001 0000
\(+ A_1\) 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
\(+ A_2\) 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0
\(+ A_3\) 3 3 2 2 2 2 1 1 2 2 1 1 1 1 0 0
\(+ A_4\) 4 3 3 2 3 2 2 1 3 2 2 1 2 1 1 0
\(- A_1 \land A_2\) 3 2 2 1 3 2 2 1 3 2 2 1 2 1 1 0
\(- A_1 \land A_3\) 2 1 2 1 2 1 2 1 3 2 2 1 2 1 1 0
\(- A_1 \land A_4\) 1 1 1 1 1 1 1 1 3 2 2 1 2 1 1 0
\(- A_2 \land A_3\) 0 0 1 1 1 1 1 1 2 1 2 1 2 1 1 0
\(- A_2 \land A_4\) -1 0 0 1 1 1 1 1 1 1 1 1 2 1 1 0
\(- A_3 \land A_4\) -2 0 0 1 0 1 1 1 0 1 1 1 1 1 1 0
\(+ A_2 \land A_3 \land A_4\) -1 0 0 1 0 1 1 1 1 1 1 1 1 1 1 0
\(+ A_1 \land A_3 \land A_4\) 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0
\(+ A_1 \land A_2 \land A_4\) 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0
\(+ A_1 \land A_2 \land A_3\) 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0
\(- A_1 \land A_2 \land A_3 \land A_4\) 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0

参考链接:

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;

int c[4], n, d[4], s;
long long f[maxn];

int main() {
    for (int i = 0; i < 4; i ++) scanf("%d", c+i);
    f[0] = 1;
    for (int i = 0; i < 4; i++)
        for (int j = c[i]; j < maxn; j++) f[j] += f[j-c[i]];
    scanf("%d", &n);
    while (n--) {
        for (int i = 0; i < 4; i++) scanf("%d", d+i);
        scanf("%d", &s);
        long long ans = f[s];
        for (int i = 0; i < 4; i++) {
            int tmp = s - (d[i] + 1) * c[i];
            if (tmp >= 0) ans -= f[tmp];
        }
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < i; j++) {
                int tmp = s - (d[i] + 1) * c[i] - (d[j] + 1) * c[j];
                if (tmp >= 0) ans += f[tmp];
            }
        }
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < i; j++) {
                for (int k = 0; k < j; k++) {
                    int tmp = s - (d[i] + 1) * c[i] - (d[j] + 1) * c[j] - (d[k] + 1) * c[k];
                    if (tmp >= 0) ans -= f[tmp];
                }
            }
        }
        int tmp = s;
        for (int i = 0; i < 4; i++)
            tmp -= (d[i] + 1) * c[i];
        if (tmp >= 0) ans += f[tmp];
        printf("%lld\n", ans);
    }
    return 0;
}
posted @ 2022-08-21 10:37  quanjun  阅读(119)  评论(0编辑  收藏  举报