[bzoj1042] [HAOI2008]硬币购物

[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;
}

传送门

posted @ 2017-05-06 19:17  Exbilar  阅读(...)  评论(...编辑  收藏