硬币购物
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<=1000
Output
每次的方法数
Sample Input
1 2 5 10 2
3 2 3 1 10
1000 2 2 2 900
Sample Output
4
27
哇还有这种操作
新的容斥姿势get
思路:
考虑没有限制的情况下,令
f[i]
为
s=i
时的答案。
则很显然
f[i]=∑f[i−cj] (j=1,2,3,4)
。
考虑有限制的情况下,则答案可以表示为:所有方案-不合法方案。
考虑容斥,则答案=所有方案-至少一个不合法的方案+至少两个不合法的方案-…
可以发现,对于至少一个不合法,可以取
∑4j=1f[i−c[j]∗(d[j]+1)]
的值作为其方案,因为很明显这样做至少使
c[j]
的数量至少为
d[j]+1
,即强制令j不合法,其他咱不管,后面会容斥掉的。
对于至少两个或以上的方案不合法,也可以用这种方法。
预处理
f
<script type="math/tex" id="MathJax-Element-977">f</script>数组后直接算即可~
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0' || '9'<ch)ch=getchar();
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x;
}
const int N=100009;
int c[9],d[9],s,tot;
ll f[N],ans;
inline void dfs(int dep,int fl,int sum)
{
if(sum<0)return;
if(dep==5)
{
ans+=fl*f[sum];
return;
}
dfs(dep+1,fl,sum);
dfs(dep+1,-fl,sum-c[dep]*(d[dep]+1));
}
int main()
{
for(int i=1;i<=4;i++)
c[i]=read();
tot=read();
f[0]=1;
for(int i=1;i<=4;i++)
for(int j=c[i];j<N;j++)
f[j]+=f[j-c[i]];
for(int i=1;i<=tot;i++)
{
for(int j=1;j<=4;j++)
d[j]=read();
s=read();
ans=0;
dfs(1,1,s);
printf("%lld\n",ans);
}
return 0;
}