星星之火

[]HAOI2008] 硬币购物 解题报告 ( 完全背包+容斥原理)

题目链接:https://www.luogu.org/problemnew/show/P1450

题目描述:

题解:

如果去掉限制的话,这就是一个完全背包。

我们可以考虑先去掉限制,把这个完全背包做出来。

这个时候我们先考虑一种硬币超出了限制,其他硬币任意的情况。怎么处理成这种情况呢?我们假设当前超过的硬币是i,我们强制用di+1枚硬币,那么剩下的就是一个

完全背包了,这个完全背包的值就是我们现在算的第i种硬币超了,其他任意的方案数。

然后我们考虑容斥,第一种第二种都超额、第一种第三种都超额、第一种第四种都超额、第二种第三种都超额、第二种第四种都超额、第三种第四种都超额的方案在上一步中都被减了两次,所以额外都加一次回来,容斥下去就行了

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;

int tot;
int c[5],d[5];
ll f[100050];
int main()
{
    for (int i=1;i<=4;i++) scanf("%d",c+i);
    scanf("%d",&tot);
    f[0]=1;
    for (int i=1;i<=4;i++)
        for (int j=c[i];j<=100050;j++) f[j]+=f[j-c[i]];
    while (tot--)
    {
        for (int i=1;i<=4;i++)
        scanf("%d",d+i);
        int s;
        scanf("%d",&s);
        ll res=0;
        for (int i=0;i<15;i++)
        {
            ll t=s;
            int cnt=0;
            for (int j=1;j<=4;j++) if ((i>>(j-1))&1) {t-=c[j]*(d[j]+1);cnt^=1;}
            if (t<0) continue;
            if (!cnt) res+=f[t];else res-=f[t];
        }
        printf("%lld\n",res);
    }
    return 0;
}

 

posted @ 2018-08-28 16:08  星星之火OIer  阅读(156)  评论(0编辑  收藏  举报