洛谷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;
}