解答
根据容斥原理
[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;
}
}