[BZOJ1042][HAOI2008]硬币购物
sol
首先考虑如果没有硬币数量的限制该怎么做。
直接上背包吧。
直接上背包存在的问题就是不知道硬币的使用数量有没有超出限制。
那就容斥一波。
考虑每种硬币,强制其超出限制,也就是强制先使用\(d_i+1\)个该种硬币,然后剩下队随便选。
时间复杂度是\(O(V+q*2^4)\),其中\(V\)是背包上限(十万即可)。
code
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi()
{
ll x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e5+5;
ll c[4],f[N],q,d[4],s;
ll k[16]={1,-1,-1,1,-1,1,1,-1,-1,1,1,-1,1,-1,-1,1};
int main()
{
for (int i=0;i<4;++i) c[i]=gi();
f[0]=1;
for (int i=0;i<4;++i)
for (int j=c[i];j<=100000;++j)
f[j]+=f[j-c[i]];
q=gi();
while (q--)
{
for (int i=0;i<4;++i) d[i]=gi();
s=gi();ll ans=0;
for (int i=0;i<16;++i)
{
ll sum=0;
if (i&1) sum+=(d[0]+1)*c[0];
if (i&2) sum+=(d[1]+1)*c[1];
if (i&4) sum+=(d[2]+1)*c[2];
if (i&8) sum+=(d[3]+1)*c[3];
ans+=k[i]*(sum>s?0:f[s-sum]);
}
printf("%lld\n",ans);
}
return 0;
}