容斥原理--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\)
简单来说,就是被包含奇数次的加在一起减去被包含偶数次的,就是全集
对于这道题,我们可以用所有方案减不合法的方案
-
所有方案:完全背包
-
不合法方案:包含(至少)一个不合法,两个不合法,三个不合法,四个不合法四种互相包含的情况,容斥求
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;
}