[HAOI2008]硬币购物,bzoj1042,无限背包+容斥

正题

      Dp做得我心力交瘁。

      这题我们可以先把无限个硬币的方案数搞出来。

      然后再容斥一下。

      关于这个容斥,就是-至少1个超过限制的+至少两个超过限制的-至少三个超过限制的+四个都超过限制的。

      算的时候就直接用f[s-\sum (d_i+1)*c_i]就可以了,因为剩下的东西的方案数已经算好了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
 
int tot,s;
long long d[5],c[5];
long long f[100010];
 
long long get1(int x){
    if((d[x]+1)*c[x]<=s) return f[s-(d[x]+1)*c[x]];
    else return 0; 
}
 
long long get2(int x,int y){
    if((d[x]+1)*c[x]+(d[y]+1)*c[y]<=s) return f[s-(d[x]+1)*c[x]-(d[y]+1)*c[y]];
    else return 0;
}
 
long long get3(int x,int y,int z){
    if((d[x]+1)*c[x]+(d[y]+1)*c[y]+(d[z]+1)*c[z]<=s) return f[s-(d[x]+1)*c[x]-(d[y]+1)*c[y]-(d[z]+1)*c[z]];
    else return 0;
}
 
long long get4(){
    if((d[1]+1)*c[1]+(d[2]+1)*c[2]+(d[3]+1)*c[3]+(d[4]+1)*c[4]<=s) return f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]];
    else return 0;
}
 
int main(){
    scanf("%lld %lld %lld %lld %d",&c[1],&c[2],&c[3],&c[4],&tot);
    f[0]=1;
    for(int i=1;i<=4;i++)
        for(int j=c[i];j<=100000;j++)
            f[j]+=f[j-c[i]];
    long long ans=0;
    while(tot--){
        scanf("%lld %lld %lld %lld %d",&d[1],&d[2],&d[3],&d[4],&s);
        ans=f[s]+get2(1,2)+get2(1,3)+get2(1,4)+get2(2,3)+get2(2,4)+get2(3,4)+get4();
        ans-=get1(1)+get1(2)+get1(3)+get1(4)+get3(1,2,3)+get3(1,2,4)+get3(2,3,4)+get3(1,3,4);
        printf("%lld\n",ans);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值