[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
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
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; }