• bzoj1042 [HAOI2008]硬币购物


    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;
    }
    
  • 相关阅读:
    CSS知识(18---21.CSS完毕)
    CSS 知识(13---)
    CSS编辑知识 ( 1---12 )
    CSS 选择器
    前端CSS
    修建泳池的题解
    2020-3-7模拟赛题解
    奶牛排队的题解
    数的拆分的题解
    青蛙王子的题解
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12517223.html
Copyright © 2020-2023  润新知