传送门
题意简述:有四种面值的硬币,现在qqq次询问(q≤1000)(q\le1000)(q≤1000),每次给出四种硬币的使用上限问最后刚好凑出sss块钱的方案数(s≤100000)(s\le100000)(s≤100000).
思路:先跑完全背包预处理出所有硬币都无限制时候的答案。
然后每次询问的时候枚举容斥掉多算的情况即可。
代码:
#include<bits/stdc++.h>
using namespace std;
long long tot,c[5],d[5],s,dp[100005];
inline long long f(long long i){return c[i]*(d[i]+1);}
int main(){
scanf("%lld%lld%lld%lld%lld",&c[1],&c[2],&c[3],&c[4],&tot);
dp[0]=1;
for(int i=1;i<=4;++i)for(int j=c[i];j<=100005;++j)dp[j]+=dp[j-c[i]];
while(tot--){
long long ans=0;
scanf("%lld%lld%lld%lld%lld",&d[1],&d[2],&d[3],&d[4],&s);
ans=dp[s];
if(s-f(1)>=0)ans-=dp[s-f(1)];
if(s-f(2)>=0)ans-=dp[s-f(2)];
if(s-f(3)>=0)ans-=dp[s-f(3)];
if(s-f(4)>=0)ans-=dp[s-f(4)];
if(s-f(1)-f(2)>=0)ans+=dp[s-f(1)-f(2)];
if(s-f(1)-f(3)>=0)ans+=dp[s-f(1)-f(3)];
if(s-f(1)-f(4)>=0)ans+=dp[s-f(1)-f(4)];
if(s-f(2)-f(3)>=0)ans+=dp[s-f(2)-f(3)];
if(s-f(2)-f(4)>=0)ans+=dp[s-f(2)-f(4)];
if(s-f(3)-f(4)>=0)ans+=dp[s-f(3)-f(4)];
if(s-f(1)-f(2)-f(3)>=0)ans-=dp[s-f(1)-f(2)-f(3)];
if(s-f(4)-f(2)-f(3)>=0)ans-=dp[s-f(4)-f(2)-f(3)];
if(s-f(1)-f(2)-f(4)>=0)ans-=dp[s-f(1)-f(2)-f(4)];
if(s-f(1)-f(4)-f(3)>=0)ans-=dp[s-f(1)-f(4)-f(3)];
if(s-f(1)-f(2)-f(3)-f(4)>=0)ans+=dp[s-f(1)-f(2)-f(3)-f(4)];
printf("%lld\n",ans);
}
return 0;
}