解析
呜呜呜不废啊
我只会跑n遍多重背包
感觉非常神仙的一道题
之所以只是蓝的可能是因为代码实现难度太低了吧
但感觉思想真的很难想到
也可能是我太菜了
容斥相关还是需要加强啊qwq
考虑如果没有硬币个数的限制的情况
显然就是个简单的完全背包了
然而如今有了硬币个数的限制
所以我们要考虑容斥
利用总方案数-硬币个数超过限制的方案数求出答案
前面的总方案数就是完全背包的dp值,关键就是对后面硬币个数超过限制方案数的求解
考虑第iii种硬币超过限制,那么就至少要使用(di+1)(d_i+1)(di+1)个,剩下可以自由选的价值是s−ci∗(di+1)s-c_i*(d_i+1)s−ci∗(di+1)
那么对应的方案数就是dps−ci∗(d[i]+1)dp_{s-c_i*(d[i]+1)}dps−ci∗(d[i]+1)
但是这样会算重丫
比如某种方案可能会同时超过两种限制,被减了两次
因此我们就开始容斥的常用套路,再加上超过两种限制的,再减去超过三种限制的,再加回来四种限制都超过的
问题得以解决
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define il inline
#define debug(a,b) fprintf(stderr,a,b)
const int N=3e5+100;
const double eps=1e-9;
inline ll read() {ll x=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}int n;
ll c[5],d[5],s;
ll dp[N],o=100000;
inline int calc(int x){int res(0);while(x){res+=x&1;x>>=1;}return res;
}
int mi[6];
int main() {
#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);
#endiffor(int i=1;i<=4;i++) c[i]=read();n=read();mi[0]=1;for(int i=1;i<=4;i++) mi[i]=mi[i-1]<<1;dp[0]=1;for(int k=1;k<=4;k++){int w=c[k];for(int i=w;i<=o;i++) dp[i]+=dp[i-w];}//for(int i=0;i<=10;i++) printf("%lld ",dp[i]);putchar('\n');while(n--){for(int i=1;i<=4;i++) d[i]=read();s=read();ll ans=0;for(int i=0;i<16;i++){int cnt=calc(i);ll tot=s;for(int k=1;k<=4;k++){if(i&mi[k-1]) tot-=c[k]*(d[k]+1);}if(tot<0) continue;ans+=pow(-1,cnt)*dp[tot];//printf("\ni=%d cnt=%d tot=%lld ans=%lld\n",i,cnt,tot,ans);}printf("%lld\n",ans);}return 0;
}
/*
1 2 5 10 1
3 2 3 1 10
*/