【bzoj1042】[HAOI2008]硬币购物
首先使用DP预处理,先求出,在不考虑每种硬币个数的限制的情况下,每个钱数有多少种拼凑方案。
为了避免重复的方案被转移,所以我们以硬币种类为第一层循环,这样阶段性的增加硬币。
一定要注意这个第一层循环要是硬币种类,并且初始 f[0] = 1。
之后对于每个询问 (A1, A2, A3, A4, S) ,根据容斥原理,我们要求的答案 Ans 就是 f[S] - (硬币1超限制的方案数) - (硬币2超限制的方案数) - (硬币3超限制的方案数) - (硬币4超限制的方案数) + (硬币1,2超限制的方案数) + (硬币1,3超限制的方案数) + (硬币1,4超限制的方案数) + .... - (硬币1,2,3超限制的方案数) - ... + (硬币1,2,3,4超限制的方案数) 。
怎样求硬币1超限制的方案数呢?我们只要先固定取 (A1+1) 个硬币1,剩余的钱数随便取就可以了,就是 f[S - (A1+1) * V[1]] 。
其余的情况都类似。
容斥的部分使用搜索实现。
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; #define MAXN 100010 typedef long long LL; int n; int x; int a[7],b[7]; LL ans; LL f[MAXN]; void dfs(int x,int k,int d) { if (d<0) return ; if (x==5) { if (k & 1) ans-=f[d]; else ans+=f[d]; return ; } dfs(x+1,k+1,d-(a[x]+1)*b[x]); dfs(x+1,k,d); } int main() { for (int i=1;i<=4;i++) scanf("%d",&b[i]); scanf("%d",&n); f[0]=1; for (int i=1;i<=4;i++) for (int j=b[i];j<=MAXN;j++) f[j]+=f[j-b[i]]; for (int i=1;i<=n;i++) { for (int j=1;j<=4;j++) scanf("%d",&a[j]); scanf("%d",&x); ans=0; dfs(1,0,x); printf("%lld\n",ans); } return 0; }
还有一个鬼畜算法。。搞不清楚啊。。
用容斥原理做背包。
首先,我们要先处理出四种钞票都不限的方案数。
对于每一个询问,我们利用容斥原理,答案为:得到S所有超过数量限制的方案数-硬币1超过限制的方案数-硬币2超过限制的方案数-硬币3超过限制的方案数-硬币4超过限制的方案数+硬币1、2超过限制的方案数+…+硬币1、2、3、4均超过限制的方案数。
而对于每种方案数的求法,也非常简单:假设我们要求的是F[S],则硬币1超过限制(即硬币1取的个数≥d[1]+1,不考虑硬币2、3、4是否超过限制)时的方案数即为F[S-(d[1]+1)×c[1]]。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int c[5]; long long F[110000]; struct{long long operator[](int pos){return pos<0?0:F[pos];}}f; int main(int argc, char *argv[]) { int T;scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&T); F[0]=1; for(int i=1;i<=4;i++) for(int j=0;j<=100000;j++) if(j+c[i]<=100000)F[j+c[i]]+=F[j]; while(T--) { int d[5],s;scanf("%d%d%d%d%d",&d[1],&d[2],&d[3],&d[4],&s); long long ans=f[s]; ans-=f[s-(d[1]+1)*c[1]]; ans-=f[s-(d[2]+1)*c[2]]; ans-=f[s-(d[3]+1)*c[3]]; ans-=f[s-(d[4]+1)*c[4]]; ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]]; ans+=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]]; ans+=f[s-(d[1]+1)*c[1]-(d[4]+1)*c[4]]; ans+=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]]; ans+=f[s-(d[2]+1)*c[2]-(d[4]+1)*c[4]]; ans+=f[s-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; ans-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]]; ans-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[4]+1)*c[4]]; ans-=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; ans-=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; #ifdef ONLINE_JUDGE printf("%lld\n",ans); #else printf("%I64d\n",ans); #endif } return 0; }
貌似更快一些= =