我们先考虑最最朴素的dp,像什么第几种硬币用了几枚的方法数,显然单次O(n^4)的吧,跑到宇宙毁灭的优秀算法233。
如何优化呢?显然,如果硬币没有限制的话,就是一个十分简单的dp了
f[0]=1;
for(long long i=1;i<=4;i++)
for(long long j=c[i];j<=N;j++)
f[j]+=f[j-c[i]];
之后加上了数量限制,怎么办呢?
强行突破限制,上容斥定理,都没超出限制的方案数=无限制的-一个超的+两个超的。。。。
超的话,只需要超一个就可以了。
#include<bits/stdc++.h>
#define N 100000
using namespace std;
long long ans,f[N+1];
long long c[5],tot,d[5],s;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline long long read()
{
long long x=0,b=1;
char c=nc();
for(;!(c<='9'&&c>='0');c=nc())if(c=='-')b=-1;
for(;c<='9'&&c>='0';c=nc())x=x*10+c-'0';
return x*b;
}
inline void init()
{
f[0]=1;
for(long long i=1;i<=4;i++)
for(long long j=c[i];j<=N;j++)
f[j]+=f[j-c[i]];
return;
}
inline void write(long long x)
{
char buf[25];
long long len=0;
if(!x)putchar('0');
else
{
while(x)buf[++len]=x%10+'0',x/=10;
for(long long i=len;i>=1;i--)putchar(buf[i]);
}
putchar('\n');
}
inline void get(long long pos,long long tmp,long long sum)
{
if(sum<0)return;
if(pos==5){
long long b=(tmp&1)?-1:1;
ans+=(long long)f[sum]*b;
return;
}
get(pos+1,tmp+1,sum-(d[pos]+1)*c[pos]);
get(pos+1,tmp,sum);
return;
}
int main()
{
//freopen("in.txt","r",stdin);
for(long long i=1;i<=4;i++)c[i]=read();tot=read();
init();
for(long long i=1;i<=tot;i++)
{
for(long long j=1;j<=4;j++)d[j]=read();s=read();
ans=0,get(1,0,s),write(ans);
}
return 0;
}