首页 > 其他 > 详细

bzoj1042 [HAOI2008]硬币购物

时间:2020-03-18 14:35:24      阅读:42      评论:0      收藏:0      [点我收藏+]

bzoj1042 [HAOI2008]硬币购物

Description

硬币购物一共有4种硬币。面值分别为c1,c2,c3,c4。某人去商店买东西,去了tot次。每次带di枚ci硬币,买si的价值的东西。请问每次有多少种付款方法。

Input

第一行 c1,c2,c3,c4,tot 下面tot行 d1,d2,d3,d4,s,其中di,s<=100000,tot<=1000

Output

每次的方法数


背包预处理+容斥
最近学了学容斥原理,感觉这类题都好难想
可能做多了就好了,刚学dp的时候也是这样


先考虑如果没有每种硬币数量的限制的情况,那么就是一个完全背包
所以可以\(O(s)\)预处理出\(f_i\)表示价值为\(i\)时,用任意多的硬币,有几种方案
然后在把限制加进去,要用到容斥
答案为\(f_i\)减去有一种硬币的数量超限,加上有两种硬币的数量超限,减去有三种硬币的数量超限,加上有四种硬币的数量超限
那么考虑如何得到每一种硬币数量超限的方案数
设这是第\(i\)种,想让它超限,就至少选\(d_i+1\)个这种硬币
那么它的价值就是\(c_i\times (d_i+1)\),剩下的价值就是\(s-c_i\times (d_i+1)\)
所以方案数是\(f_{c_i\times (d_i+1)}\),当然如果\(s<c_i\times (d_i+1)\),方案数肯定为0
所以询问复杂度\(O(1)\)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
    int x=0,y=1;
    char c=std::getchar();
    while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    return y?x:-x;
}
int n;
int c[5],d[5],tmp[5];
LL f[100006];
int main(){
    c[1]=read();c[2]=read();c[3]=read();c[4]=read();n=read();
    f[0]=1;
    for(reg int j=1;j<5;j++)
        for(reg int i=1;i<=1e5;i++)
            if(i>=c[j]) f[i]+=f[i-c[j]];
    while(n--){
        d[1]=read();d[2]=read();d[3]=read();d[4]=read();
        int s=read();
        LL ans=f[s];
        for(reg int i=1;i<5;i++) tmp[i]=(d[i]+1)*c[i];
        for(reg int i=1;i<5;i++)
            if(s>=tmp[i]) ans-=f[s-tmp[i]];
        for(reg int i=1;i<5;i++)
            for(reg int j=i+1;j<5;j++)
                if(s>=tmp[i]+tmp[j])
                    ans+=f[s-tmp[i]-tmp[j]];
        if(s>tmp[1]+tmp[2]+tmp[3]) ans-=f[s-tmp[1]-tmp[2]-tmp[3]];
        if(s>tmp[1]+tmp[3]+tmp[4]) ans-=f[s-tmp[1]-tmp[3]-tmp[4]];
        if(s>tmp[2]+tmp[3]+tmp[4]) ans-=f[s-tmp[2]-tmp[3]-tmp[4]];
        if(s>tmp[1]+tmp[2]+tmp[4]) ans-=f[s-tmp[1]-tmp[2]-tmp[4]];
        if(s>tmp[1]+tmp[2]+tmp[3]+tmp[4]) ans+=f[s-tmp[1]-tmp[2]-tmp[3]-tmp[4]];
        std::printf("%lld\n",ans);
    }
    return 0;
}

bzoj1042 [HAOI2008]硬币购物

原文:https://www.cnblogs.com/suxxsfe/p/12517223.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!