BZOJ_1042_[HAOI2008]硬币购物_容斥原理+背包
题意:
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。
分析:
假设没有di的限制,先跑一遍完全背包
容斥,用总方案数减去有一种硬币数目不合法的方案数加上有两种硬币不合法的方案数......
怎么求这个方案数呢?
我们发现如果第i种硬币数目不合法,那它一定拿了至少(di+1)个,方案数就是f[n-(di+1)*ci]
代码:
#include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define LL long long #define N 100000 LL c[5], tot, d[5], s; LL f[N+10]; int main(){ scanf("%lld%lld%lld%lld%lld", &c[1], &c[2], &c[3], &c[4], &tot); int i,j; f[0] = 1; for(i = 1;i <= 4 ;i++) { for(j = c[i];j <= N;j++) { f[j] += f[j - c[i]]; } } while(tot--) { scanf("%lld%lld%lld%lld%lld", &d[1], &d[2], &d[3], &d[4], &s); LL ans = f[s]; LL g1 = (d[1] + 1) * c[1]; LL g2 = (d[2] + 1) * c[2]; LL g3 = (d[3] + 1) * c[3]; LL g4 = (d[4] + 1) * c[4]; if(s - g1 >= 0) ans -= f[s - g1]; if(s - g2 >= 0) ans -= f[s - g2]; if(s - g3 >= 0) ans -= f[s - g3]; if(s - g4 >= 0) ans -= f[s - g4]; if(s - g1 - g2 >= 0) ans += f[s - g1 - g2]; if(s - g1 - g3 >= 0) ans += f[s - g1 - g3]; if(s - g1 - g4 >= 0) ans += f[s - g1 - g4]; if(s - g2 - g3 >= 0) ans += f[s - g2 - g3]; if(s - g2 - g4 >= 0) ans += f[s - g2 - g4]; if(s - g3 - g4 >= 0) ans += f[s - g3 - g4]; if(s - g1 - g2 - g3 >= 0) ans -= f[s - g1 - g2 - g3]; if(s - g1 - g2 - g4 >= 0) ans -= f[s - g1 - g2 - g4]; if(s - g1 - g3 - g4 >= 0) ans -= f[s - g1 - g3 - g4]; if(s - g2 - g3 - g4 >= 0) ans -= f[s - g2 - g3 - g4]; if(s - g1 - g2 - g3 - g4 >= 0) ans += f[s - g1 - g2 - g3 - g4]; printf("%lld\n",ans); } }