[bzoj1042][HAOI2008]硬币购物【dp】【容斥】

【题目描述】

Description

  硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买s
i的价值的东西。请问每次有多少种付款方法。

Input

  第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000

Output

  每次的方法数

Sample Input

1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900

Sample Output

4
27

HINT

Source

【题解】

 记 f[i] 表示不考虑数量限制买价值为i的物品的方案数。

由容斥原理可得 ans=f[i]-有一种越界的+有两种越界的-有三种越界的+有四种越界的

越界的的计算方法:把它在限制内的全去掉,剩下的任意。

/* --------------
    user Vanisher
    problem bzoj-1042
----------------*/
# include <bits/stdc++.h>
# define 	ll 		long long
# define 	S 		100000
using namespace std;
ll read(){
	ll tmp=0, fh=1; char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
	return tmp*fh;
}
ll c[5],d[5],n,f[S+10],s,ans;
int main(){
	c[1]=read(), c[2]=read(), c[3]=read(), c[4]=read(), n=read();
	f[0]=1;
	for (ll i=1; i<=4; i++)
		for (ll j=c[i]; j<=S; j++)
			f[j]=f[j]+f[j-c[i]];
	for (ll i=1; i<=n; i++){
		d[1]=read(), d[2]=read(), d[3]=read(), d[4]=read(); s=read();
		ans=0;
		for (ll j=0; j<(1<<4); j++){
			ll now=s,ti=1;
			for (ll k=1; k<=4; k++)
				if ((j&(1<<(k-1)))>0) now=now-(d[k]+1)*c[k],ti=-ti;
			if (now>=0) ans=ans+ti*f[now];
		}
		printf("%lld\n",ans);
	}
	return 0;
}


posted @ 2018-01-27 19:40  Vanisher  阅读(...)  评论(... 编辑 收藏