题解 P1450 [HAOI2008]硬币购物
Posted wxy-max
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P1450 [HAOI2008]硬币购物相关的知识,希望对你有一定的参考价值。
[HAOI2008]硬币购物
共有四种硬币,其面值分别为(c_1,c_2,c_3,c_4)
(n)次询问,每次给定每种硬币的个数(D_i)和付款金额(S),问共有多少种付款方式
(n≤10^3,S≤10^5)
暴力做法
我们可以把问题看作做(n)次多重背包,用单调队列优化,最优的复杂度为(O(n4s))在这里不详细讨论
完全背包+容斥
对于每次询问,相当于给定四个限制,第(i)个限制即只能至多使用(i)个硬币支付。类似于错排,满足限制的方案数=全部方案数-不满足的方案数
(错排的容斥推导可见https://www.luogu.com.cn/blog/maxsama/zuhemath)
而对于全部的方案数,相当于做体积为(S)的完全背包,而(S)至多为(10^5),因此我们可以(O(4S))预处理所有(S)。
而对于不满足条件的方案数
类似于错排,若不满足其中一个限制条件,一定是非法的,我们可以设(A_i)为第(i)种硬币不满足限制的方案集合。
那么不满足条件的方案数即
而对于(|A_i|),我们当前一共要支付(S)个硬币,而对于第(i)种只能支付(D_i)个,那么我们如果先把(D_i+1)个硬币付完剩下的支付方案(即从四种硬币中支付(S-C_i(D_i+1))的金额)是一定不合法的,因为第(i)个硬币至少需要选零个,而如果选(0)个,最终还是多付了一个。
因此,第(|A_i|=F[S-C_i(D_i+1)])
因为我们要求集合的并,因此,我们需要考虑容斥,而考虑容斥我们就需要求出集合的交
我们考虑任意最简单的情况:两个集合的交
我们从定义出发,(A_i)为第(i)种硬币不满足限制的方案集合,而(A_j)为第(j)种硬币不满足限制的方案集合。
而显然,同时满足(i)种硬币不满足限制与(j)种硬币不满足限制这样的性质的集合,为它们的交。
同样地,我们可以把他们两个的(D_i+1)都先支付出去,那么则有
那么
那么我们可以状压枚举(T)算出答案。
总的复杂度为(O(4S+16n))
代码:
#include <iostream>
using namespace std;
const int maxn = 10;
int c[maxn],d[maxn];
long long f[100010];
int main(){
int n;
cin >> c[1] >> c[2] >> c[3] >> c[4] >> n;
f[0] = 1;
for (int i = 1; i <= 4; i++){
for (int j = c[i]; j <= 100010; j++){
f[j] += f[j - c[i]];
}
}
while(n--){
int s;
cin >> d[1] >> d[2] >> d[3] >> d[4] >> s;
long long ans = f[s];
for (int i = 0; i < 1 << 4; i++){
long long sum = 0,cnt = 0;
for (int j = 0; j < 4; j++){
if (i >> j & 1){
sum += c[j + 1] * (d[j + 1] + 1);
cnt++;
}
}
//cout << sum << endl;
long long t = s - sum;
if (cnt % 2 == 0 && t >= 0 && sum != 0) ans += f[s - sum];
else if (t >= 0 && sum != 0) ans -= f[s - sum];
}
cout << ans << endl;
}
//system("PAUSE");
return 0;
}
以上是关于题解 P1450 [HAOI2008]硬币购物的主要内容,如果未能解决你的问题,请参考以下文章