[BZOJ1042][HAOI2008]硬币购物

bzoj
luogu

sol

首先考虑如果没有硬币数量的限制该怎么做。
直接上背包吧。
直接上背包存在的问题就是不知道硬币的使用数量有没有超出限制。
那就容斥一波。
考虑每种硬币,强制其超出限制,也就是强制先使用\(d_i+1\)个该种硬币,然后剩下队随便选。
时间复杂度是\(O(V+q*2^4)\),其中\(V\)是背包上限(十万即可)。

code

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi()
{
    ll x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
const int N = 1e5+5;
ll c[4],f[N],q,d[4],s;
ll k[16]={1,-1,-1,1,-1,1,1,-1,-1,1,1,-1,1,-1,-1,1};
int main()
{
    for (int i=0;i<4;++i) c[i]=gi();
    f[0]=1;
    for (int i=0;i<4;++i)
        for (int j=c[i];j<=100000;++j)
            f[j]+=f[j-c[i]];
    q=gi();
    while (q--)
    {
        for (int i=0;i<4;++i) d[i]=gi();
        s=gi();ll ans=0;
        for (int i=0;i<16;++i)
        {
            ll sum=0;
            if (i&1) sum+=(d[0]+1)*c[0];
            if (i&2) sum+=(d[1]+1)*c[1];
            if (i&4) sum+=(d[2]+1)*c[2];
            if (i&8) sum+=(d[3]+1)*c[3];
            ans+=k[i]*(sum>s?0:f[s-sum]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2018-03-27 14:11  租酥雨  阅读(...)  评论(... 编辑 收藏