容斥原理--P1450 [HAOI2008]硬币购物

公式:\(\mid \bigcup\limits_{i=1}^nS_i\mid = \sum\limits_{m=1}^n{(-1)}^{m-1}\sum\limits_{a_i<a_{i+1}} \mid \bigcap\limits_{i=1}^m S_{a_i}\mid\)

简单来说,就是被包含奇数次的加在一起减去被包含偶数次的,就是全集

对于这道题,我们可以用所有方案减不合法的方案

  1. 所有方案:完全背包

  2. 不合法方案:包含(至少)一个不合法,两个不合法,三个不合法,四个不合法四种互相包含的情况,容斥求

code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 1e5+10;
int c[5],n;
int d[5],s;
int dp[maxn];
signed main(){
	for (int i = 1;i <= 4;i++) c[i] = read();n = read();
	dp[0] = 1;
	for (int i = 1;i <= 4;i++){
		for (int j = 1;j <= 100000;j++){
			if(j >= c[i]) dp[j] += dp[j-c[i]];
		}
	}
	while (n--){
		for (int i = 1;i <= 4;i++) d[i] = read();s = read();
		int ans = dp[s],res = 0;
		for (int i = 1;i < 16;i++){
			int tmp = 0,cnt = 0;
			for (int j = 1;j <= 4;j++){
				if (i&(1<<j>>1)){
					cnt++;
					tmp += (d[j]+1)*c[j];
				}
			}
			if (tmp > s) continue;
			if (cnt&1) res += dp[s-tmp];
			else res -= dp[s-tmp];
		}
		printf("%lld\n",ans-res);
	}
	return 0;
}
posted @ 2020-11-23 20:50  小又又yyyy  阅读(85)  评论(0编辑  收藏  举报