[bzoj1042] [HAOI2008]硬币购物
Description
硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。
Input
第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s
Output
每次的方法数
HINT
数据规模
di,s<=100000
tot<=1000
第一眼看过去,这是一道求背包方案数的问题,这个我们大家都会求.但是题目显然不会有那么简单.第一眼数据范围感觉像是\(O(N)\)的,对于每次询问\(O(1)\)出解,这样感觉差不多.好了,我们的思路回到如何如何预处理上去.由于C1,C2,C3,C4是提前告诉你了的,但由于D1,D2,D3,D4,s没有告诉你.所以我们无法根据询问处理出答案.但是我们能处理出没有限制的硬币数情况下的方案总数.所以我们考虑由非限制硬币数的答案转移到限制的硬币数的答案.于是我们想到了容斥原理.我们算出了所有的非限制硬币数的答案,那么我们还可以算出每个硬币超限的方案数,那么我们就可以通过容斥原理来实现了.那么答案就是:总超限购买的方案数[sum]-硬币1超限购买的方案数...+硬币1和硬币2超限购买的方案数...最后我们考虑每一个硬币超限购买的方案数.假设是硬币x,我们优先用D[x]+1个硬币x,刚好超限.假设当前算到的剩余钱数是sum,那么方案就是f[sum-(D[x]+1)*C[x]].具体过程通过dfs实现方便一些.
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
static const int maxm=5;
static const int size=1e6+10;
int D[maxm],C[maxm];
LL f[size];
int T,tot;
LL ans;
void dfs(int x,int k,int sum){
if(sum<0)return;
if(x==5){
if(k&1)ans-=f[sum];
else ans+=f[sum];
return;
}
dfs(x+1,k,sum);
dfs(x+1,k+1,sum-(D[x]+1)*C[x]);
}
int main(){
for(int i=1;i<=4;i++)scanf("%d",&C[i]);
scanf("%d",&T);
f[0]=1;
for(int i=1;i<=4;i++)
for(int v=C[i];v<=100000;v++)
f[v]+=f[v-C[i]];
while(T--){
for(int i=1;i<=4;i++)scanf("%d",&D[i]);
scanf("%d",&tot);ans=0;
dfs(1,0,tot);
printf("%lld\n",ans);
}
return 0;
}