传送门c++
根据容斥原理spa
其中\(S_i\)表示第\(i\)种元素超限的取的方法集合,交的初始值是\(U\)。(\(U\)表全集,\(\overline A\)表\(A\)在\(U\)下的补集)code
如何求出\(\left|\bigcap_{j=1}^k S_{i_j}\right|\)?
考虑先求出彻底背包的dp值
而后强制将第\(i_j\)个元素选取超过\(d_{i_j}\)个。
这样的方案总数为\(dp[t]-dp[t-\sum_{j=1}^k(d_i+1)c_i]\)。(\(dp[]\)的负数项为\(0\))ci
而后就能够愉快地容斥了。get
#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; } }