[HAOI2008]硬幣購物-題解

傳送門c++

解答

根據容斥原理spa

\[\left|\bigcap_{i=1}^n \overline{S_i}\right| = |U| - \left|\bigcup_{i=1}^n S_i\right| = \sum_{0 \le k\le n}(-1)^k\sum_{1\le i_1<\cdots<i_k\le n}\left|\bigcap_{j=1}^k S_{i_j}\right| \]

其中\(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;
	}
}
相關文章
相關標籤/搜索