BZOJ1042:[HAOI2008]硬币购物(DP,容斥)

时间:2021-05-01 22:13:15

Description

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

Input

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

Output

每次的方法数

Sample Input

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

Sample Output

4
27

Solution

先设$f[i]$表示没有硬币数量限制的时候,花费为$i$的方案数。
简单容斥可得,$ans=$没有限制的情况$-$一种硬币使用大于$d[i]$次的方案$+$两种硬币使用大于$d[i]$次的方案$-$三种硬币使用大于$d[i]$次的方案$+$四种硬币使用大于$d[i]$次的方案。
爆搜枚举所有情况。假设只有$c[1]$使用大于$d[1]$次,那么方案为$f[s-(d[1]+1)*c[1]]$。也就是强制先使用上$d[1]+1$枚硬币,其余的随意选。
其他情况同理。

Code

 #include<iostream>
#include<cstdio>
#define N (100009)
#define LL long long
using namespace std; LL T,s,ans,c[],d[],f[N]; void Dfs(LL x,LL k,LL sum)
{
if (sum<) return;
if (x==)
{
if (k&) ans-=f[sum];
else ans+=f[sum];
return;
}
Dfs(x+,k+,sum-(d[x]+)*c[x]);
Dfs(x+,k,sum);
} int main()
{
f[]=;
for (int i=; i<=; ++i)
{
scanf("%lld",&c[i]);
for (int j=c[i]; j<=; ++j)
f[j]+=f[j-c[i]];
}
scanf("%lld",&T);
while (T--)
{
for (int i=; i<=; ++i)
scanf("%lld",&d[i]);
scanf("%lld",&s);
ans=;
Dfs(,,s);
printf("%lld\n",ans);
}
}