bzoj1042: [HAOI2008]硬币购物

来源:互联网 发布:linux运维最佳实践 pdf 编辑:程序博客网 时间:2024/06/05 17:03

题面在这里
做法:
暴力dp显然要挂。
我们考虑先完全背包预处理出无限制的方案数。
然后考虑只有一个硬币有限制的情况。我们需要的答案应该等于f[tot]f[tot(bi+1)ai],假定tot是总钱数,a是钱的面值,b是限制。至于为什么的话,一大佬题解里讲得挺清楚。链接
那么多种硬币都有限制就用一下容斥原理即可。

/*************************************************************    Problem: bzoj 1042 [HAOI2008]硬币购物    User: fengyuan    Language: C++    Result: Accepted    Time: 44 ms    Memory: 2068 kb    Submit_Time: 2017-12-14 14:57:58*************************************************************/#include<bits/stdc++.h>#define rep(i, x, y) for (int i = (x); i <= (y); i ++)#define down(i, x, y) for (int i = (x); i >= (y); i --)#define mid ((l+r)/2)#define lc (o<<1)#define rc (o<<1|1)#define pb push_back#define mp make_pair#define PII pair<int, int>#define F first#define S second#define B begin()#define E end()using namespace std;typedef long long LL;//headconst int N = 100010;int n, m;LL f[N];int a[10], b[10];inline LL get(LL x){    if (x > m) return 0;    return f[m-x];}int main(){    rep(i, 1, 4) scanf("%d", &a[i]); scanf("%d", &n);    f[0] = 1;    rep(i, 1, 4)        rep(j, a[i], 100000) f[j] += f[j-a[i]];    while (n --){        rep(i, 1, 4) scanf("%d", &b[i]);        scanf("%d", &m); LL ret = 0;        rep(i, 0, 15){            int t = 0, sum = 0;            rep(j, 1, 4)                if (((i>>j-1)&1)){                    t ++; sum += (b[j]+1)*a[j];                }            if (t&1) ret -= get(sum); else ret += get(sum);        }        printf("%lld\n", ret);    }    return 0;}